I want to implement business logic in DbSet derived classes. I like the idea of not having services and DAL abstractions and think this could be a good way. For this to work I need to inject objects into my DbSet but I don't know how. Here some sample code which does not work, because the EF Framework can't create an object of the DbSet. Maybe someone can point me in the right direction?
public class LongTermBookingDbSet : DbSet<LongTermBooking>
{
DbContext _dbContext { get; set; }
public LongTermBookingDbSet(DbContext dbContext )
{
this._dbContext = _bContext ;
}
public override LongTermBooking Add(LongTermBooking entity)
{
return this.Add(entity, false);
}
public LongTermBooking Add(LongTermBooking entity, bool SendMails)
{
var dbSet = base.Add(entity);
//do something with the _dbContext
return dbSet;
}
}
One of the options is to aggregate real DbSet, not derive it:
public class PersonSet : IDbSet<Person>
{
private readonly DbSet<Person> _dbSet;
public PersonSet(DbSet<Person> dbSet)
{
_dbSet = dbSet;
}
}
public class MyDbContext: DbContext
{
public PersonSet PersonSet {...}
}
Inherits from DbSet<T> with the purposes to add property
Related
myDbContext: DbContext
{
DbSet<Person> {get;set;}
//So many domain
}
So I used Set<T> approch
myDbContext: DbContext
{
pubilc DbSet<T> Set<Person> table()
{
return Set<T>
}
}
But now migration is not generating changes or tables
I am using Julie Lerman's EF Repository techniques.
All my entities implement the following interface
public interface IEntity
{
EntityState State { get; set; }
}
All my repositories call the following GetList function
public virtual IList<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties)
{
List<T> list;
IQueryable<T> dbQuery = ((DbContext)context).Set<T>();
//Apply eager loading
foreach (var navigationProperty in navigationProperties)
{
dbQuery = dbQuery.Include(navigationProperty);
}
list = dbQuery.AsNoTracking().Where(where).ToList();
return list;
}
I am finding that the initial state property for my entities is zero, but I want to set it to
I want to set State property to be EntityState.Unchanged
How should I do this?
Julie Lerman described it in her book Programming Entity Framework: DbContext At page 93
(Example 4-15).
You can use following code in your DbContext constractor to set the object states to UnChanged:
public YourContext()
{
((IObjectContextAdapter)this).ObjectContext .ObjectMaterialized +=
(sender, args) =>
{
var entity = args.Entity as IEntity;
if (entity != null)
{
entity.State = State.Unchanged;
}
}
}
Here's another easier approach to this issue. I am using it and it works!!
public abstract class Entity<TId> : BaseEntity, IEntity<TId>, IModelState
{
public virtual TId Id { get; private set; }
public byte[] RowVersion { get; protected set; }
private readonly IDictionary<Type, IEvent> events = new Dictionary<Type, IEvent>();
public IEnumerable<IEvent> Events => events.Values;
public ModelState ModelState { get; protected set; } = ModelState.Unchanged;
protected Entity()
{
ModelState = ModelState.Added;
}
... removed for brevity
make sure you're using C# 7 and Roslyn Compiler with your .NET 4.6.x
I think this is safer because only your own Entity Object will have the power to set it to unchanged when it gets initialized by EF. In my opinion DbContext should not have the right to set the "state" property of any entity.
Is there a way to inherits from DbSet? I want to add some new properties, like this:
public class PersonSet : DbSet<Person>
{
public int MyProperty { get; set; }
}
But I don't know how to instantiate it in my DbContext
public partial MyContext : DbContext
{
private PersonSet _personSet;
public PersonSet PersonSet
{
get
{
_personSet = Set<Person>(); // Cast Error here
_personSet.MyProperty = 10;
return _personSet;
}
}
}
How can I achieve this?
I have found an answer that works for me. I declare my DbSet properties as my derived interface in my context, e.g.:
IDerivedDbSet<Customer> Customers { get; set; }
IDerivedDbSet<CustomerOrder> CustomerOrders { get; set; }
My implementation includes a private IDbSet which which is assigned in the constructor e.g.:
public class DerivedDbSet<T> : IDerivedDbSet<T> where T : class
{
private readonly IDbSet<T> _dbSet;
public DerivedDbSet(IDbSet<T> dbSet)
{
this._dbSet = dbSet;
}
...
}
My implementation of a derived DbContext interface hides the Set<>() method like so:
new public IDerivedSet<TEntity> Set<TEntity>() where TEntity : class
{
//Instantiate _dbSets if required
if (this._dbSets == null)
{
this._dbSets = new Dictionary<Type, object>();
}
//If already resolved, return stored reference
if (this._dbSets.ContainsKey(typeof (TEntity)))
{
return (IDerivedSet<TEntity>) this._dbSets[typeof (TEntity)];
}
//Otherwise resolve, store reference and return
var resolvedSet = new GlqcSet<TEntity>(base.Set<TEntity>());
this._dbSets.Add(typeof(TEntity), resolvedSet);
return resolvedSet;
}
The derived DbContext returns a newly constructed IDerivedSet or picks it's reference cached in a Dictionary. In the derived DbContext I call a method from the constructor which uses type reflection to go through the DbContexts properties and assigns a value/reference using it's own Set method. See here:
private void AssignDerivedSets()
{
var properties = this.GetType().GetProperties();
var iDerivedSets =
properties.Where(p =>
p.PropertyType.IsInterface &&
p.PropertyType.IsGenericType &&
p.PropertyType.Name.StartsWith("IDerivedSet") &&
p.PropertyType.GetGenericArguments().Count() == 1).ToList();
foreach (var iDerivedSet in iDerivedSets)
{
var entityType = iDerivedSet.PropertyType.GetGenericArguments().FirstOrDefault();
if (entityType != null)
{
var genericSet = this.GetType().GetMethods().FirstOrDefault(m =>
m.IsGenericMethod &&
m.Name.StartsWith("Set") &&
m.GetGenericArguments().Count() == 1);
if (genericSet != null)
{
var setMethod = genericSet.MakeGenericMethod(entityType);
iDerivedSet.SetValue(this, setMethod.Invoke(this, null));
}
}
}
}
Works a treat for me. My context class has navigable set properties of my set type that implements a derived interface inheriting IDbSet. This means I can include query methods on my set type, so that queries are unit testable, instead of using the static extensions from the Queryable class. (The Queryable methods are invoked directly by my own methods).
One solution is to create a class that implements IDbSet and delegates all operations to a real DbSet instance, so you can store state.
public class PersonSet : IDbSet<Person>
{
private readonly DbSet<Person> _dbSet;
public PersonSet(DbSet<Person> dbSet)
{
_dbSet = dbSet;
}
public int MyProperty { get; set; }
#region implementation of IDbSet<Person>
public Person Add(Person entity)
{
return _dbSet.Add(entity);
}
public Person Remove(Person entity)
{
return _dbSet.Remove(entity);
}
/* etc */
#endregion
}
Then in your DbContext, put a getter for your Custom DbSet:
public class MyDbContext: DbContext
{
public DbSet<Person> People { get; set; }
private PersonSet _personSet;
public PersonSet PersonSet
{
get
{
if (_personSet == null)
_personSet = new PersonSet( Set<Person>() );
_personSet.MyProperty = 10;
return _personSet;
}
set
{
_personSet = value;
}
}
}
I solved this using another variable to instantiate the "regular" DbSet.
private DbSet<Person> _persons { get; set; }
public PersonDbSet<Person> Persons { get { return new PersonDbSet(_persons); } }
This way entityframework recognizes the Entity but I can still use my own DbSet class.
I know this is really old and the OP has probably moved on but I was just wondering the same thing myself. EF populates the DbSets inside your MyContext at run time.
I just created MyDbSet<T> that inherits from DbSet<T> and the replaced all references to DbSet<T> with my derived class in MyContext. Running my program failed to instantiate any of the properties.
Next I tried setting the properties to IDbSet<T> since DbSet<T> implements this interface. This DOES work.
Investigating further, the constructors for DbSet are protected and internal (the protected one calls the internal one anyway). So MS have made it pretty hard to roll your own version. You may be able to access the internal constructors through reflection but chances are that EF will not construct your derived class anyway.
I would suggest writing an extension method to plug the functionality into the DbSet object, however you're stuck if you want to store state.
I have a generic repository an I am trying to add a GetById method as shown here
C# LINQ to SQL: Refactoring this Generic GetByID method
The problem is my repository does not use System.Data.Linq.DataContext
instead I use System.Data.Entity.DbContext
So I get errors where I try to use
Mapping.GetMetaType
and
return _set.Where( whereExpression).Single();
How can I implement a generic GetById method in CTP5? Should I be using System.Data.Entity.DbContext in my Repository.
Here is the start of my repository class
public class BaseRepository<T> where T : class
{
private DbContext _context;
private readonly DbSet<T> _set;
public BaseRepository()
{
_context = new MyDBContext();
_set = _context.Set<T>();
}
The most basic approach is simply
public T GetById(params object[] keys)
{
_set.Find(keys);
}
If you know that all your entities have primary key called Id (it doesn't have to be called Id in DB but it must be mapped to property Id) of defined type you can use simply this:
public interface IEntity
{
int Id { get; }
}
public class BaseRepository<T> where T : class, IEntity
{
...
public T GetById(int id)
{
_set.Find(id);
}
}
If data type is not always the same you can use:
public interface IEntity<TKey>
{
TKey Id { get; }
}
public class BaseRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>
{
...
public TEntity GetById(TKey id)
{
_set.Find(id);
}
}
You can also simply use:
public class BaseRepository<TEntity, TKey> where TEntity : class
{
...
public TEntity GetById(TKey id)
{
_set.Find(id);
}
}
try this
public virtual T GetByID(object id)
{
// Define the entity key values.
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("Id", id) };
string qualifiedEntitySetName = _context.DefaultContainerName + "." + typeof(T).Name;
EntityKey key = new EntityKey(qualifiedEntitySetName, entityKeyValues);
return (T)_context.GetObjectByKey(key);
}
i have this Dependency resolver
public class NinjectDependencyResolvercs : IDependencyResolver
{
private readonly IResolutionRoot resolutionRoot;
public NinjectDependencyResolvercs(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
return resolutionRoot.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return resolutionRoot.GetAll(serviceType);
}
}
in global.asax.cs
// Ninject DI container ----------------------------------------------------------- |
public void SetupDependencyInjection()
{
// Create Ninject DI kernel
IKernel kernel = new StandardKernel();
#region Register services with Ninject DI Container
// DbContext to SqlDataContext
kernel.Bind<DbContext>()
.To<SqlDataContext>();
// IRepository to SqlRepository
kernel.Bind<IRepository>()
.To<SqlRepository>();
// IUsersServices to UsersServices
kernel.Bind<IUsersServices>()
.To<UsersServices>();
// IMessagesServices to MessagesServices
kernel.Bind<IMessagesServices>()
.To<MessagesServices>();
// IJobAdvertsServices to JobAdvertsServices
kernel.Bind<IJobAdvertsServices>()
.To<JobAdvertsServices>();
#endregion
// Tell ASP.NET MVC 3 to use Ninject DI Container
DependencyResolver.SetResolver(new NinjectDependencyResolvercs(kernel));
}
// --------------------------------------------------------------------------------- |
and class
public class SqlDataContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<JobAdvert> JobAdverts { get; set; }
public DbSet<Message> Messages { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Roles).WithMany(x => x.Users).Map(x =>
{
x.MapLeftKey(y => y.UserId, "UserId");
x.MapRightKey(y => y.RoleId, "RoleId");
x.ToTable("UsersInRoles");
});
base.OnModelCreating(modelBuilder);
}
}
all dependecies work fine but for DbContext to SqlDataContext is problem. If use this:
public class SqlRepository
{
private DbContext dataContext;
public SqlRepository(DbContext dataContext) {
this.dataContext = dataContext;
}
public DbSet<User> Users {
get {
return dataContext.Users;
}
}
}
then
dataContext.Users
and all others properties alert this error:
'System.Data.Entity.DbContext' does not contain a definition for 'JobAdverts' and no extension method 'JobAdverts' accepting a first argument of type 'System.Data.Entity.DbContext' could be found (are you missing a using directive or an assembly reference?)
Have anyone any idea why DI doent work for Class DbContext ?
If I understand correctly, You're injecting DbContext which doesn't have those methods/properties, as they're declared in the derived type SqlDataContext.
You need to inject the SqlDataContext. If you want to use an interface, you'll need to extract an interface from SqlDataContext.
EDIT:
Ninject binds at runtime while the errors you're getting (I presume) are at compile time. You could get around this by using the dynamic key word, but that's just working AROUND the problem.
public class SqlRepository
{
private dynamic dataContext;
public SqlRepository(DbContext dataContext) {
this.dataContext = dataContext;
}
...
}
What you need to do is change the signature to use your SqlDataContext:
public class SqlRepository
{
private SqlDataContextdata Context;
public SqlRepository(SqlDataContextdata Context) {
this.dataContext = dataContext;
}
...
}
because DbContext does not contain those methods, only your SqlContext does. and your sqlcontext is bound to DbContext at runtime.