so I am trying to make a generic function for a where query, not using repository
so it is possible to do something like this?
public IEnumerable<T> Something<T>(int authorId) where T : class
{
return Vmsb.Set<T>().Where(c => c.AuthorId== authorId);
}
now I can't because it dont know what c.AuthorId is
Create an interface IHaveAuthor and specify it on partial classes with this property:
public interface IHaveAuthor
{
int AuthorId { get; set; }
}
//Note that the interface is already implemented in auto-generated part.
//Or if it's Code First, just specify it directly on your classes.
public partial class Book : IHaveAuthor
{
}
public partial class Article : IHaveAuthor
{
}
Then point the interface under the generic type where constraint:
public IEnumerable<T> GetAuthorPublicationsOf<T>(int authorId)
where T : class, IHaveAuthor
{
return Vmsb.Set<T>().Where(c => c.AuthorId == authorId);
}
And the usage:
var authorBooks = query.GetAuthorPublicationsOf<Book>(authorId);
var authorArticles = query.GetAuthorPublicationsOf<Article>(authorId);
Adding on to Olexander's answer, since EF recommends you use the Unit of Work pattern, I usually don't assume a DbContext in my methods - I pass in the most generic object possible instead. Also just as a matter of style, I like to return the interface.
EDIT Updated to include Olexander's important fix to use IQueryable instead of IEnumerable.
So my method signature would look like:
public IQueryable<IHaveAuthor> Something(int authorId, IQueryable<IHaveAuthor> items)
{
return items.Where(c => c.AuthorId == authorId);
}
So calling this would be a bit different than your current calls to it - presumably something like:
var db = new MyDbContext();
var items = db.Books;
var itemForAuthor1 = Something(1, items);
Otherwise your "Something" method isn't terribly flexible - it assumes a single existing DbContext on your current object which might not be a safe assumption (since it's only supposed to live as long as this small chunk of work, whatever it is), you can't chain it with other commands, etc.
Diego hope my code helps u.
protected List<T> ListAll<T>() where T : class
{
using (MyDbContext db = new MyDbContext ())
{
return db.Set(typeof(T)).Cast<T>().AsNoTracking<T>().ToList();
}
}
protected T ListAllById<T>(int id) where T : class
{
using (MyDbContext db = new MyDbContext ())
{
return db.Set(typeof(T)).Cast<T>().Find(id);
}
}
protected void InsertObj(Object obj)
{
using (MyDbContext db = new MyDbContext())
{
db.Set(obj.GetType()).Add(obj);
db.SaveChanges();
}
}
protected void UpdateObj(Object obj)
{
try
{
using (MyDbContext db = new MyDbContext())
{
db.Set(obj.GetType()).Attach(obj);
db.Entry(obj).State = EntityState.Modified;
db.SaveChanges();
}
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show(" " + ex.Message);
}
}
protected void DeleteObj(Object obj)
{
using (MyDbContext db = new MyDbContext ())
{
db.Set(obj.GetType()).Attach(obj);
db.Entry(obj).State = EntityState.Deleted;
db.SaveChanges();
}
}
Related
I have a series of classes that do almost the same thing. Here's what 2 of the prospective child classes look like:
class ItemWriter
{
private List<Item> items;
public ItemWriter(List<Item> items)
{
this.items= items;
}
public List<Item> Write()
{
using (var ctx = new DatabaseEntities())
{
using(var trx = ctx.Database.BeginTransaction())
{
ctx.Item.AddRange(items);
rowsAdded = ctx.SaveChanges();
trx.Commit();
}
}
return items;
}
}
And another...
class ItemShelfWriter
{
private List<ItemShelf> itemShelfs;
public ItemShelfWriter(List<ItemShelf> itemShelfs)
{
this.itemShelfs= itemShelfs;
}
public List<ItemShelf> Write()
{
using (var ctx = new DatabaseEntities())
{
using(var trx = ctx.Database.BeginTransaction())
{
ctx.TtemShelf.AddRange(itemShelfs);
ctx.SaveChanges();
trx.Commit();
}
}
return itemShelfs;
}
}
You can see that both classes do almost the same thing - only they write to different tables and the Write() methods have different return types.
I'd like to have an interface / abstract class IWriter.cs that forces every child class to have a Write() method returning a type and a constructor accepting a List. If I could remove some of the code duplication in Write() that would be good.
However, the issue is that each class takes and returns different objects and writes to a different database table. Is there any way I can achieve / improve this? I have a vague understanding that it's probably generics that need to be used.
That sounds like a good case for generics:
Interface:
public interface IWriter<T>
{
List<T> Write();
}
If you can remove code duplicates depends on what your real classes actually do in detail. From the code you've shown you could do this:
class Writer<T> : IWriter<T>
{
private List<T> items;
public Writer(List<T> items)
{
this.items= items;
}
public List<T> Write()
{
using (var ctx = new DatabaseEntities())
{
using(var trx = ctx.Database.BeginTransaction())
{
ctx.Item.AddRange(items);
rowsAdded = ctx.SaveChanges(); // what is rowsAdded??
trx.Commit();
}
}
return items;
}
}
You did not declare rowsAdded, so I don't know what to do with this.
You can now instantiate this class like:
var itemWriter = new Writer<Item>(items);
var itemShelfWriter = new Writer<ItemShelf>(itemShelfs);
Im stuck on a generics issue.
I have a method that returns DbSet from a DbContext object based on the object type.
Now I want to write I method where I pass in a object that implements a interface called IBaseData (that all entities implements). I want to use the existing method to return the correct DbSet based on the type of the IBaseData object, but I can't get it to work.
I've tried various versions of , typeof and gettype.
Existing method:
public GenericRepository<T> GetRepo<T>() where T : class
{
return new GenericRepository<T>(_context);
}
New method (this is just what Im trying to do):
public async Task SetDateLastInvoked<T>(IBaseData data, int id)
{
var tmp = GetRepo<typeof(data)>().Where(obj => obj.Id.Equals(id));
tmp.DateLastInvoked = DateTime.Now;
await SaveChangesAsync();
}
I assume you are doing something like:
public interface IBaseData
{
int Id { get; set; }
}
Then a method would looe like
public GenericRepository<T> GetRepo<T>()
where T : class
{
return new GenericRepository<T>(_context);
}
public async Task SetDateLastInvoked<T>(int id)
where T : class, IBaseData
{
var tmp = GetRepo<T>().FirstOrDefault(obj => obj.Id == id));
tmp.DateLastInvoked = DateTime.Now;
await SaveChangesAsync();
}
Usage:
public MyClass : IBaseData
{
public Id { get; set }
}
SetDateLastInvoked<MyClass>(1);
Ended up with this, based on previous answer:
public async Task SetLastInvoked<T>(int id) where T : class, IBaseData
{
var tmp = await GetRepo<T>().Single(obj => obj.Id.Equals(id));
tmp.DateLastInvoked = DateTime.Now;
await SaveChangesAsync();
}
I also created another method based on the same pattern:
public async Task DeleteItem<T>(int id, bool removeRow) where T : class, IBaseData
{
if (removeRow)
{
GetRepo<T>().Delete(await GetRepo<T>().Single(obj => obj.Id.Equals(id)));
}
else
{
var tmp = await GetRepo<T>().Single(obj => obj.Id.Equals(id));
tmp.Active = false;
}
await SaveChangesAsync();
}
I have a "DatabaseController" Class, which contains several repository classes, each for every table in a database, and they only perform crud operations. My issue is this.
When i delete an entry , i call the corresponding repository method. I also need to call some delete methods from other tables (repositories). What is the best approach to do this?
Example Code:
DatabaseController Class
public DatabaseController(){
this.dbContext = new WSATDbContext();
}
private IWSATGenericRepository<Restmethod> _restmethod ;
private IWSATGenericRepository<Soapservice> _soapservice ;
public IWSATGenericRepository<Restmethod> Restmethod
{
get
{
if (_restmethod == null)
{
_restmethod = new RestmethodRepository(dbContext);
}
return _restmethod;
}
}
public IWSATGenericRepository<Soapservice> Soapservice
{
get
{
if (_soapservice == null)
{
_soapservice = new SoapserviceRepository(dbContext);
}
return _soapservice;
}
}
Example Repository:
public class RestmethodRepository : IWSATGenericRepository<Restmethod>
{
public RestmethodRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("Null DbContext");
this.dbContext = dbContext;
this.dbSet = dbContext.Set<Restmethod>();
}
private DbContext dbContext { get; set; }
private DbSet<Restmethod> dbSet { get; set; }
public Restmethod get(int id)
{
Restmethod restmethod = dbSet.Find(id) ;
dbContext.Entry(restmethod).Collection(s => s.methodkeywords).Load() ;
return restmethod ;
}
public Restmethod delete(int id)
{
var entity = this.get(id);
//I want to call some other repo methods here
dbContext.Entry(entity).State = EntityState.Deleted;
try
{
dbContext.SaveChanges();
}
catch (DbUpdateException e)
{
System.Diagnostics.Debug.WriteLine(e.StackTrace);
return null;
}
return entity;
}
}
}
EDIT: Is it good practice to pass the "DatabaseController" UOW class as an argument to each repository? Are there any theoretical problems with that? This is an academic project , so i'd like to stay as formal as possible.
My Problem is the following.
I have a DataAccessLayer Project with a DalClass. In this Dal class I have many Methods for operations with Entity Framework. For example some pseudo-code to show how we do it.
class DalClass
{
public void SetEntityObject(EntityObject entityObject)
{
using (var context = new Entities())
{
context.EntityObjectSet.Attach(entityObject);
context.ChangeTracker.DetectChanges();
foreach (var entity in context.ChangeTracker.Entries())
{
entity.State = entityObject.EntityState;
}
context.SaveChanges();
}
}
public EntityObject GetEntitObject(Guid id)
{
EntityObject result;
using (var context = new Entities())
{
result = context.EntityObjectSet.Where(x => x.Id == Id);
}
return result;
}
}
Each time we do operations with EF we create a new instance context and do some work. My problem is that I have a method in my DalClass to get a List of EntityObjects Like this :
public List<EntityObject> GetEntityObjectsByIds(List<Guid> idList)
{
using (var context = new Entities())
{
var query = from entityObject in context.EntityObjectSet
where idList.Contains(entityObject.Id)
select entityObject;
return query.ToList();
}
}
After I return the List to my Business Layer class. I do some changes to each EntityObject, calculations, change some Property values and so on. After that I call a method from my BusinessLayer class to save the new edited List with this method:
public void UpdateEntityObjects(List<EntityObject> newEntityObjectList)
{
using (var context = new Entities())
{
var idList = entityObjectList.Select(x => x.Id).ToList();
var query = from entityObject in context.EntityObjectSet
where idList.Contains(entityObject.Id)
select entityObject;
var entityObjectList = query.ToList();
entityObjectList = newEntityObjectList;
context.ChangeTracker.DetectChanges();
foreach (var entity in context.ChangeTracker.Entries())
{
entity.State = EntityState.Modified;
}
context.SaveChanges();
}
}
Problem
I can not save newEntityObjectList that I Edited in my BusinessLayer. Obviously this is not working.
entityObjectList = newEntityObjectList;
So how is the best way to save the newEntityObjectList ?
Problem
I have to fier this query twice
var query = from entityObject in context.EntityObjectSet
where idList.Contains(entityObject.Id)
select entityObject;
Solution to 2. Problem could be if I use one Context instance for GetEntityObjectsByIds() and UpdateEntityObjects(). But in all our Dal Classes we avoided that. Even if I use one instance of Context class, this does not solve Problem 1. So how can I save newEntityObjectList ?
Well, my DAL class would most definetely be something like this:
class EntityObjectDAL
{
private ObjectContext/DbContext context = new ObjectContext/DbContext();
public List<EntityObject> GetObjects() { return context.EntityObjects.ToList(); }
// Tracked by context. Just change it in BL
public void SaveObjects() { context.SaveChanges(); }
// Just call SaveChanges(), no need for anything special...
public void InsertObject(EntityObject eo) { context.EntityObjects.Add(eo); }
public void UpdateObject(EntityObject eo) { context.Entry(eo).State = EntityState.Modified; }
}
Keep it simple...
I have many methods like this:
public IEnumerable<Director> GetAllDirectors()
{
using (var dbContext = new BodModelContainer())
{
return dbContext.Members.OfType<Director>().ToList();
}
}
..or this..
public Member GetMemberById(int memberId)
{
using(var dbContext = new BodModelContainer())
{
return dbContext.Members.Find(new[] {memberId});
}
}
or that:
public SomeType GetOrDoSomethingWithDbBySomethingElse(T1 t1var, T2, T2 var, ...)
{
using(var dbContext = new BodModelContainer())
{
return dbContext.SomeType.DoSomething();
}
}
So I wanna reorganize all of these methods something like:
// create db context here
public void InDb(Func<BodModelContainer, out SomeGenericType???> workingWithDb)
{
using(var dbContext = new BodModelContainer())
{
workingWithDb(dbContext);
}
}
// using it like
public Member GetMemberById(int memberId)
{
InDb((dbContext) =>
{
return dbContext.Members.Find(new[] { memberId });
});
}
Question: How to make such generic InDb method, what is SomeGenericType???
The actual problem it looks like you're describing is the fact that you can't figure out how to set the return type of the Func<>. To be able to make this generic you'll need to make InDb require a generic type as well.
public TRet InDb<TRet>(Func<BodModelContainer, TRet> workingWithDb) {
TRet ret = default(TRet);
using (var dbContext = new BodModelContainer()) {
ret = workingWithDb(dbContext);
}
return ret;
}
public Member GetMemberById(int memberId) {
return InDb(dbContext => { return dbContext.Members.Find(new[] { memberId }); });
}
Should work (all the code here is untested), or using anonymous methods you could have a local variable and make .Net do all the nasty work.
public void InDb(Action<BodModelContainer> workingWithDb) {
using (var dbContext = new BodModelContainer()) {
workingWithDb(dbContext);
}
}
public Member GetMemberById(int memberId) {
Member member;
InDb(dbContext => { member = dbContext.Members.Find(new[] { memberId }); });
return member;
}
Of course, this all being said, I don't know if this level of redirection/abstraction is useful - you're making it slightly more complicated with little perceptible gain (if any). Unless there is a lot of work setting up and tearing down the dbContext used in InDb I don't think it is very helpful.