Ok, I might be punching above my pay grade here, but I'm trying to create a generic CRUD routine for and EF project. I've got most of it working but I'm flailing around on one point.
Normally you do something like this to add an entity through a context-
DBContext.MyClass.Add( p ); // p = instance of MyClass
That works fine, but since in a global method to handle all adds regardless of what class they are I'm passing in a Model as an object it would look more like this-
DBContext<whateverobject>.Add(whateverobject); // my objects is an object param passed into the method
I've tried doing a bunch of typeofs and there where T : class stuff but I'm having no luck. Any pointing in the right direction would help me out.
I'm using EF Core 2 so my options might also be more limited than EF 6.
Thanks.
The method you're looking for is DbContext's Set<T>()
Your generic repository for your generic CRUD would look something like this:
public class Repo<T> where T: class
{
private readonly DbSet<T> _set;
public Repo(DbContext dbContext)
{
_set = dbContext.Set<T>();
}
public void Add(T entity) => _set.Add(entity);
}
This example includes a maybe unusual thing:
where T: class: we have to specify that T has to be a reference type because DbSet<T> expects T to be a reference type
For generic querying you might want to use extension methods.
In order to implement a ById method you'd have to specify that the type T must have an Id property using an interface. That would look something like this:
public interface IEntity
{
int Id { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
}
public static class DbSetExtensions
{
public static T ById<T>(this DbSet<T> dbSet, int id) where T: class =>
dbSet.FirstOrDefault(entity => entity.Id == id);
}
Related
my current task needs to pay attention on mapping between different object types and so I recognized the very nice AutoMapper library.
So far easy to handle but these different objects contains complex interface type properties. Let me show you a code snippet:
public inferface IInterface
{
string TextProperty { get; set;}
}
public class A : IInterface
{
string TextProperty { get; set; }
}
public class B : IInterface
{
string TextProperty { get; set; }
}
public inferface IComplexInterface
{
IInterface ComplexProperty { get; set; }
}
public class ComplexA : IComplexInterface
{
IInterface ComplexProperty { get; set; }
}
public class ComplexB : IComplexInterface
{
IInterface ComplexProperty { get; set; }
}
In my case it is possible that class A is mapped to class B and vice versa.
Mapping from type A to B is no problem by configuring CreateMap<A, B>();
Mapping from class ComplexA to class ComplexB throws an exception:
Error mapping types.
Mapping types:
ComplexA -> ComplexB
NamespaceOfComplexA.ComplexA -> NamespaceOfComplexB.ComplexB
Type Map configuration:
ComplexA -> ComplexB
NamespaceOfComplexA.ComplexA -> NamespaceOfComplexB.ComplexB
Property:
ComplexProperty
A possible solution I already found here on stackoverflow could be a configuration as follows:
CreateMap<A, IInterface>().As<B>();
CreateMap<B, IInterface>().As<A>();
But in my case it is not working.
Any suggestions?
Now, I found a solution that works for me.
I use AutoMapper with a non generic approach and so I configure via:
CreateMap(typeof(ComplexA), typeof(ComplexB))
To consider properties with complex types like interfaces or even abstract classes it is possible to write an own ValueResolver that has to implement the interface:
IValueResolver<object, object, object>
with following method:
public object Resolve(object source, object destination, object destMember, ResolutionContext context)
{
//...
}
To resolve interface/abstract class properties you can configure your types by enhancing the configuration with the method ForMember(...) and define a conrete ValueResolver for the particular property as follows:
CreateMap(typeof(ComplexA), typeof(ComplexB)).ForMember("ComplexProperty", x => x.ResolveUsing(new ValueResolver(/*...*/)));
In my case it was the solution to map the interface property to a concrete implementation of my class definitions.
Hope it is useful.
I created a generic repository class that all my other repository classes are inheriting from. This is great, because it means almost all the plumbing is done one time for all repositories. I put a full explanation of what I'm talking about here, but here is the code for my GenericRepository (some code is removed for brevity):
public abstract class GenericRepository<T> : IGenericRepository<T> where T : class, new()
{
private IMyDbContext _myDbContext;
public GenericRepository(IMyDbContext myDbContext)
{
_myDbContext = myDbContext;
}
protected IMyDbContext Context
{
get
{
return _myDbContext;
}
}
public IQueryable<T> AsQueryable()
{
IQueryable<T> query = Context.Set<T>();
return query;
}
public virtual void Create(T entity)
{
Context.Set<T>().Add(entity);
}
public virtual void Update(T entity)
{
Context.Entry(entity).State = System.Data.EntityState.Modified;
}
}
As you see, I have a Create method and an Update method. It would be very convenient to have a "CreateOrUpdate" method, so I don't have to manually check for existing objects each time I have to save something to the database.
Each of my objects in Entity Framework have an "Id", but the challenge here is that the GenericRepository works with "T".
Now, with that rather long introduction, to my specific question.
How do I create a generic CreateOrUpdate method for my GenericRepository?
UPDATE
After Marcins response, I implemented the following generic methods in my GenericRepository. It will take some time before I can test that it works as expected, but it looks very promising.
public virtual bool Exists(Guid id)
{
return Context.Set<T>().Any(t => t.Id == id);
}
public virtual void CreateOrUpdate(T entity)
{
if (Exists(entity.Id))
{
var oldEntity = GetSingle(entity.Id);
Context.Entry(oldEntity).CurrentValues.SetValues(entity);
Update(oldEntity);
}
else
{
Create(entity);
}
}
The code above has no less than 3 roundtrips to the database when updating. I'm sure it can be optimized, but it wasn't really the exercise for this question.
This question handles that topic better:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
Create a interface with Id property, implement it on every of your entities and add another generic constraint to your class:
public interface IEntity
{
int Id { get; set;}
}
And
public abstract class GenericRepository<T> : IGenericRepository<T> where T : class, IEntity, new()
With that, you'll be able to use Id property within your generic repository class.
Of course - Id don't have to be an int, it can be Guid as well.
I have code first implementation for flowing hierarchy,
BaseContact{
Public int Id{get;set;}
public string Name{get;set;}
//..
}
Person:BaseContact{
public string Designation{get;set;}
//..
}
Company:BaseContact{
public int NumOfEmployees{get;set;}
//..
}
I want to identify person or company with by using only the Id value? Currently I am using reflection to identify whether it is a person or company. Is there any other way to identify it without doing too much?
Without seeing how you initialised your classes I'm going to assume you have a table per concrete type approach.
You can't do it just from the ID, as you don't know which table the ID belongs to. ID 2 in "Person" table is a different entity to ID 3 in "Company". The only practical way to identify only from an ID is using a Table per Hierarchy approach and inspecting the type descriptor.
Some good references
http://weblogs.asp.net/manavi/archive/2011/01/03/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete-type-tpc-and-choosing-strategy-guidelines.aspx
http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx
You can also use a simple is statement instead of reflection. Ie if (entity is Company)
In your BaseContact (assume it is an abstract class) add abstract property which will be implemented by other two classes.Use Enum to identify the property type as follows.
public enum MyType
{
Person,
Company,
};
public abstract class BaseContact{
public abstract MyType ContactType{get;}
}
public class Person:BaseContact
{
public override MyType ContactType
{
get
{
return MyType.Person;
}
}
}
public class Company:BaseContact
{
public override MyType ContactType
{
get
{
return MyType.Company;
}
}
}
Use your BaseContact repository to retrieve entities and use enum for type separation.
I want to write a rich domain class such as
public class Product
{
public IEnumerable<Photo> Photos {get; private set;}
public void AddPhoto(){...}
public void RemovePhoto(){...}
}
But the entity framework (V4 code first approach) requires an ICollection type for lazy loading! The above code no longer works as designed since clients can bypass the AddPhoto / RemovePhoto method and directly call the add method on ICollection. This is not good.
public class Product
{
public ICollection<Photo> Photos {get; private set;} //Bad
public void AddPhoto(){...}
public void RemovePhoto(){...}
}
It's getting really frustrating trying to implement DDD with the EF4. Why did they choose the ICollection for lazy loading?
How can i overcome this? Does NHibernate offer me a better DDD experience?
I think i found the solution...See here for more details: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/47296641-0426-49c2-b048-bf890c6d6af2/
Essentially you want to make the ICollection type protected and use this as the backing collection for the public IEnumerable
public class Product
{
// This is a mapped property
protected virtual ICollection<Photo> _photos { get; set; }
// This is an un-mapped property that just wraps _photos
public IEnumerable<Photo> Photos
{
get { return _photos; }
}
public void AddPhoto(){...}
public void RemovePhoto(){...}
}
For lazy loading to work the type must implement ICollection and the access must be public or protected.
You can't insert into an IEnumerable. This applies to the EF just as much as it does to your clients. You don't have to use ICollection, though; you can use IList or other writeable types. My advice to get the best of both worlds is to expose DTOs rather than entities to your clients.
You can overcome this by using the ReadOnlyCollection(Of T)
public class Product
{
private IList<Photo> _photos;
public IList<Photo> Photos {
get
{
return _photos.AsReadOnly();
}
private set { _photos = value; }
}
public void AddPhoto(){...}
public void RemovePhoto(){...}
}
EDIT:
ICollection<T> => IList<T>
Hope that is what you were looking for.
It looks like that MEF framework creates objects which have default CTOR. How about customized CTOR, or Constructor with parameters? For example:
[Export (typeof(IInterface1))]
public class MyClass : IInterface1
{
public MyClass(int id) {....}
....
}
If not, one way I can think is to pass object as parameters to CTOR. For example:
public Interface IParameterID {
public int Id { get; private set; }
...
}
Then the CTOR will be:
public MyClass([Import(typeof(IParameter))] IParameterID id)
{ ... }
Not sure if it is possible to add attribute to CTOR's parameters? And the next question is that if MEF will automatically create an instance of IParameter and inject it to the CTOR's parameter?
Yes, this is possible. Just put an [ImportingConstructorAttribute] on the constructor you would like to use. The parameters will automatically be treated as imports, but if you need to change the contract name on them you can also put an import attribute on them.