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.
Related
I currently have a multi one-to-many relationship hierarchy database tblProjects->tblLines->tblGroups->tblStations etc. And an Entity framework 6 model.
These entity framework classes all implement a base class "tblBase":
public abstract class TblBase : INotifyPropertyChanged
{
private int _id;
public int ID
{
get
{
return _id;
}
set
{
_id = value;
NotifyPropertyChanged();
}
}
private Nullable<int> _coid;
public Nullable<int> COID
{
get
{
NotifyPropertyChanged();
return _coid;
}
set
{
_coid = value;
NotifyPropertyChanged();
}
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged();
}
}
I have a treeview that allows me to select any node as the parent type, and currently I have a method for each type that allows me to reload all the child entities.
I would like to see how this could be made generic:
private async static Task<bool> RefreshLinesAsync(LocalUser ThisUser, ProjectEntities DBContext, object Entity)
{
List<object> NonExistingNodes = new List<object>();
var bContinue = false;
var PassedEntity = Entity as TblBase;
//Scan through all DB child entities and reload their DB values
foreach (var SubEntity in DBContext.tblLines.Where(x => x.ProjectID == PassedEntity.ID).ToList())
{
await DBContext.Entry(SubEntity).ReloadAsync().ContinueWith(x =>
{
if (!x.IsFaulted)
{
if ((SubEntity.COID.GetValueOrDefault() != 0) && (SubEntity.COID.GetValueOrDefault() != ThisUser.ID))
NotifyCOIDConflict(SubEntity, new CheckedOutArgs()
{
ConflictCOID = SubEntity.COID.GetValueOrDefault()
});
bContinue = true;
}
}, TaskScheduler.FromCurrentSynchronizationContext());
if (bContinue)
//Continue to child entities method
await RefreshGroupsAsync(ThisUser, DBContext, SubEntity);
}
return true;
}
private async static Task<bool> RefreshGroupsAsync(LocalUser ThisUser, ProjectEntities DBContext, object Entity)
{
List<object> NonExistingNodes = new List<object>();
var bContinue = false;
var PassedEntity = Entity as TblBase;
foreach (var SubEntity in DBContext.tblGroups.Where(x => x.LineID == PassedEntity.ID).ToList())
{
await DBContext.Entry(SubEntity).ReloadAsync().ContinueWith(x =>
{
if (!x.IsFaulted)
{
if ((SubEntity.COID.GetValueOrDefault() != 0) && (SubEntity.COID.GetValueOrDefault() != ThisUser.ID))
NotifyCOIDConflict(SubEntity, new CheckedOutArgs()
{
ConflictCOID = SubEntity.COID.GetValueOrDefault()
});
bContinue = true;
}
}, TaskScheduler.FromCurrentSynchronizationContext());
if (bContinue)
await RefreshStationsAsync(ThisUser,DBContext, SubEntity);
}
return true;
}
The only method I can see useful is Set(), although it does not provide a Where() method, which is critical since I do not want to retrieve the entire table.
You can make your functions generic. They maybe like this one:
private async static Task<bool> RefreshLinesAsync<TEntity>(LocalUser ThisUser, ProjectEntities DBContext, TEntity Entity) where TEntity : TblBase
{
List<TEntity> NonExistingNodes = new List<TEntity>();
var bContinue = false;
var PassedEntity = Entity as TblBase;
foreach (var SubEntity in DBContext.Set<TEntity>().Where(x => (x as TblBase).ProjectID == PassedEntity.ID).ToList()) {
//Your other code here...
}
}
The where clause in function definition, make you sure that this method can be called only with subclasses of TblBase.
EDIT:
I forgot to mention that you need to cast SubEntity as TblBase inside foreach loop to use it...
EDIT (in response of comments):
If you need to get all TblBase subclasses from your entity, you cannot make your function so generic if you keep them in separate tables: It will became hardly mantainable when you have to add more subclasses.
I suggest you to use a single table through Table Per Hierarchy (see this article in MSDN) changing TblBase from abstract to concrete class, then you can get all of them this way:
var allSubClassEntities = DBContext.Set<TblBase>();
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 two derived classes (Sale and ServiceCharge). Both are Transactions. If I have a BusinessService, I want to create a ServiceCharge for it. If I pass a Product, I want to instantiate Sale.
Here's my idea.
private void CreateInstance(object element)
{
Transaction transaction;
if (element.GetType() == typeof(BussinessService))
{
transaction = new ServiceCharge((BussinessService)element))
}
else
{
transaction = new Sale((Product)element);
}
{
Could you tell me a more elegant way? I would know how to use generics with only a single constructor
private void CreateInstance<T>(T element)
{
Transaction transaction = new Transaction((T)element);
}
But I don't know how to work out with the first case.
Just a plain interface would also work in this case:
interface ITransactionable
{
Transaction CreateTransaction();
}
class BusinessService : ITransactionable
{
public Transaction CreateTransaction() { return new ServiceCharge( this ); }
}
class Product : ITransactionable
{
public Transaction CreateTransaction() { return new Sale( this ); }
}
private void CreateInstance(ITransactionable element)
{
Transaction transaction = element.CreateTransaction();
...
}
Define a generic interface like this:
public interface ITransactionable<T>
where T : Transaction
{
T CreateTransaction();
}
And decorate your BussinessService and Product as:
public class BussinessService :
ITransactionable<ServiceCharge>
{
public T CreateTransaction()
{
return new ServiceCharge(this);
}
}
public class Product :
ITransactionable<Sale>
{
public T CreateTransaction()
{
return new Sale(this);
}
}
Now your generic method can be defined as:
private void CreateInstance<T>(ITransactionable<T> element)
{
Transaction transaction = element.CreateTransaction();
...
}
Just create two different methods:
private void CreateInstance(Product product)
{
Transaction transaction = new Sale(product);
}
private void CreateInstance(BusinessService service)
{
Transaction transaction = new ServiceCharge(service);
}
The compiler will know what method you called depending on the type of the parameter you use.
BusinessService and Product should be polymorphic in some way, probably by sharing a interface, somthing like
interface IChargable<out T> where T : Transaction
{
Transaction Charge();
}
The interface implemented thus,
class BusinessService : IChargable<ServiceCharge>
{
public ServiceCharge Charge()
{
return new ServiceCharge(...
}
}
class Product : IChargable<Sale>
{
public Sale Charge()
{
return new Sale(...
}
}
which means some code like this would work
var chargables = new IChargable<Transaction>[]
{
new BusinessService(),
new Product()
};
var transactions = chargables.Select(c => c.Charge());
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();
}
}
The project that I'm working on at the moment uses an IDisposable object in every method in a class. It has started getting tedious re-typing the using block at the start of every method, and was wondering if there was a way to specify a disposable variable for use in every method of the class?
public static class ResourceItemRepository
{
public static ResourceItem GetById(int id)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
// Code goes here...
}
}
public static List<ResourceItem> GetInCateogry(int catId)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
// Code goes here...
}
}
public static ResourceItem.Type GetType(int id)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
// Code goes here...
}
}
}
No, there's nothing particularly geared towards this. You could write:
public static ResourceItem GetById(int id)
{
WithDataContext(db =>
{
// Code goes here...
});
}
// Other methods here, all using WithDataContext
// Now the only method with a using statement:
private static T WithDataContext<T>(Func<TestDataContext, T> function)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
return function(db);
}
}
I'm not sure that it would be particularly beneficial though.
(Note that I've had to change it from Action<TestDataContext> in my original version to Func<TestDataContext, T> as you want to be able to return values from your methods.)
Frankly, i'd keep the verbose code, but using a snippet instead of typing it all each time.
Either create your own snippet with a special tool or use text-replacement tools like Texter
Maybe a simple refactoring is the best that you can do without resorting to something like PostSharp:
public static class ResourceItemRepository {
public static ResourceItem GetById(int id) {
using (var db = CreateDataContext()) {
// Code goes here...
}
}
public static List<ResourceItem> GetInCateogry(int catId) {
using (var db = CreateDataContext()) {
// Code goes here...
}
}
public static ResourceItem.Type GetType(int id) {
using (var db = CreateDataContext()) {
// Code goes here...
}
}
private static TestDataContext CreateDataContext() {
return DataContextFactory.Create<TestDataContext>();
}
}