Extending IEnumerable to Return BindingList - entity-framework

In a previous question on Stack Overflow, I had run into an issue with returning an EF query to the DataGridView. Of course I'd run into an issue. However, I added an extension method that still has me baffled since it isn't working. It seems like it should, but for some reason it's not.
public static class BindingListEntityExtension
{
public static BindingList<T> ToBindingList<T>(this IEnumerable<T> entities)
{
BindingList<T> rtn = new BindingList<T>();
foreach (T obj in entities)
{
rtn.Add(obj);
}
return rtn;
}
}
Any ideas what's going on? My implementation is like so:
MyEntities context = new MyEntities();
tempDataGridView.DataSource = context.Employees.ToBindingList();

Got it. As Ecyrb had suggested in a previous post, the BindingList does not sort. I did use the suggested site/ to get my list to sort. Thanks guys! My extension does work now.

Related

EF Core 2.1 In memory DB not updating records

I'm using the in memory database provider for integration tests however I don't seem to be able to update a record. I've run the same code against a real SQL database and everything gets updated fine. Here is my test fixture code.
Test Fixture:
public class TestFixture<TStartup> : IDisposable
{
private readonly TestServer _testServer;
public HttpClient TestClient { get; }
public IDatabaseService DbContext { get { return _testServer.Host.Services.GetService<DatabaseService>(); } }
public TestFixture() : this(Path.Combine("src")) { }
protected TestFixture(string relativeTargetProjectPatentDir)
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Testing");
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddDbContext<DatabaseService>(options =>
options.UseInMemoryDatabase("TestDB")
.EnableSensitiveDataLogging());
})
.UseEnvironment("Testing")
.UseStartup<Startup>();
_testServer = new TestServer(builder)
{
BaseAddress = new Uri("http://localhost:5010")
};
TestClient = _testServer.CreateClient();
TestClient.BaseAddress = _testServer.BaseAddress;
}
public void Dispose()
{
TestClient.Dispose();
_testServer.Dispose();
}
}
I've spent most of the day googling this and not come across any other people talking about it so I'm assuming its probably my issue rather than a EF bug. I'm sure someone would have noticed a DB that you can't update.
Updating works with Singleton but I have CQRS architecture and to check if the entry was updated in e2e test I have to reload entry
Context.Entry(entity).Reload();
I hope that this can help someone
It turned out that changing the lifetime of my DbContext in my test fixture to singleton solved my issue.
Well it can be that DbContext is used in wrong way. I had the same problem. I used the DbContext in same way as you. I simply returned the instance by .Host.Services.GetService<TContext>. The problem with this approach is that DbContext will never release tracked entities so either you set entity State as EntityState.Detached and you force DbContext to reload it, or you will use scopes.
using (var scope = _testServer.Host.Services.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<DatabaseService>();
//make any operations on dbContext only in scope
}
Adding to Chris's answer. Here is an example of what I had vs. what fixed the issue:
services.AddDbContext<TestDbContext>(options => {
options.UseInMemoryDatabase("TestDb");
});
to
var options = new DbContextOptionsBuilder<TestDbContext>()
.UseInMemoryDatabase(databaseName: "TestDb")
.Options;
services.AddSingleton(x => new TestDbContext(options));
Using AsNoTracking behavior may additionally work below,
services.AddDbContext<TestDbContext>(
a => a.UseInMemoryDatabase(databaseName: "TestDb").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking),
ServiceLifetime.Singleton)
Also, how are you updating record? This seems to track in EFCore InMemory,
_dbContext.Entry(modifyItem).State = EntityState.Modified;
However, this doesn't seem to work as much.
_dbContext.Entry(existingItem).CurrentValues.SetValues(modifyItem);

Unit testing with EF Code First DataContext

This is more a solution / work around than an actual question. I'm posting it here since I couldn't find this solution on stack overflow or indeed after a lot of Googling.
The Problem:
I have an MVC 3 webapp using EF 4 code first that I want to write unit tests for. I'm also using NCrunch to run the unit tests on the fly as I code, so I'd like to avoid backing onto an actual database here.
Other Solutions:
IDataContext
I've found this the most accepted way to create an in memory datacontext. It effectively involves writing an interface IMyDataContext for your MyDataContext and then using the interface in all your controllers. An example of doing this is here.
This is the route I went with initially and I even went as far as writing a T4 template to extract IMyDataContext from MyDataContext since I don't like having to maintain duplicate dependent code.
However I quickly discovered that some Linq statements fail in production when using IMyDataContext instead of MyDataContext. Specifically queries like this throw a NotSupportedException
var siteList = from iSite in MyDataContext.Sites
let iMaxPageImpression = (from iPage in MyDataContext.Pages where iSite.SiteId == iPage.SiteId select iPage.AvgMonthlyImpressions).Max()
select new { Site = iSite, MaxImpressions = iMaxPageImpression };
My Solution
This was actually quite simple. I simply created a MyInMemoryDataContext subclass to MyDataContext and overrode all the IDbSet<..> properties as below:
public class InMemoryDataContext : MyDataContext, IObjectContextAdapter
{
/// <summary>Whether SaveChanges() was called on the DataContext</summary>
public bool SaveChangesWasCalled { get; private set; }
public InMemoryDataContext()
{
InitializeDataContextProperties();
SaveChangesWasCalled = false;
}
/// <summary>
/// Initialize all MyDataContext properties with appropriate container types
/// </summary>
private void InitializeDataContextProperties()
{
Type myType = GetType().BaseType; // We have to do this since private Property.Set methods are not accessible through GetType()
// ** Initialize all IDbSet<T> properties with CollectionDbSet<T> instances
var DbSets = myType.GetProperties().Where(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>)).ToList();
foreach (var iDbSetProperty in DbSets)
{
var concreteCollectionType = typeof(CollectionDbSet<>).MakeGenericType(iDbSetProperty.PropertyType.GetGenericArguments());
var collectionInstance = Activator.CreateInstance(concreteCollectionType);
iDbSetProperty.SetValue(this, collectionInstance,null);
}
}
ObjectContext IObjectContextAdapter.ObjectContext
{
get { return null; }
}
public override int SaveChanges()
{
SaveChangesWasCalled = true;
return -1;
}
}
In this case my CollectionDbSet<> is a slightly modified version of FakeDbSet<> here (which simply implements IDbSet with an underlying ObservableCollection and ObservableCollection.AsQueryable()).
This solution works nicely with all my unit tests and specifically with NCrunch running these tests on the fly.
Full Integration Tests
These Unit tests test all the business logic but one major downside is that none of your LINQ statements are guaranteed to work with your actual MyDataContext. This is because testing against an in memory data context means you're replacing the Linq-To-Entity provider but a Linq-To-Objects provider (as pointed out very well in the answer to this SO question).
To fix this I use Ninject within my unit tests and setup InMemoryDataContext to bind instead of MyDataContext within my unit tests. You can then use Ninject to bind to an actual MyDataContext when running the integration tests (via a setting in the app.config).
if(Global.RunIntegrationTest)
DependencyInjector.Bind<MyDataContext>().To<MyDataContext>().InSingletonScope();
else
DependencyInjector.Bind<MyDataContext>().To<InMemoryDataContext>().InSingletonScope();
Let me know if you have any feedback on this however, there are always improvements to be made.
As per my comment in the question, this was more to help others searching for this problem on SO. But as pointed out in the comments underneath the question there are quite a few other design approaches that would fix this problem.

AutoMapper Map entity with self-reference entity

I have a small problem that gives me a StackOweflow problem.
I use EF 4.1 with complextypes and this works, the problem is that you need to create an instance of the complextype even if all the values are null, to make it work with EF.
So now I have a class that looks like this..
public class GoodsItem{
public GoodsItem InnerGoodsItem{get;set;}
//-- A lot of other properties needed for this class
public GoodsItem()
{
this.InnerGoodsItem = new GoodsItem();
}
}
I need the code in the constructor for EF to work as it should, but then every time I create a GoodsItem it creates a new GoodItems that creates a new GoodsItem and so on...
How can I solve this problem with AutoMapper and still keep EF 4.1 happy.
Thanks in advanced...
Can't see how that constructor could ever work. Perhaps you could change your class to initialize the inner item in the property get, like this:
public class GoodsItem{
private GoodsItem _innerGoodsItem;
public GoodsItem InnerGoodsItem
{
get
{
if (_innerGoodsItem == null) _innerGoodsItem = new GoodsItem();
return _innerGoodsItem;
}
set { _innerGoodsItem = value; }
}
//-- A lot of other properties needed for this class
public GoodsItem()
{
//No longer need this call in ctor
//this.InnerGoodsItem = new GoodsItem();
}
}
Not sure if this will cause a problem with EF though (thankfully, I've pretty much avoided EF so far!).

Autofac: Injected collection is not empty (contains one item)

I'm using Autofac 2.4.4.705.
The output of the following code is: 1 (which means the resolved collection contains one item. I thought it should be empty)
class Program
{
static void Main(string[] args)
{
var builder = new Autofac.ContainerBuilder();
builder.RegisterModule(new AutofacModule());
using (var container = builder.Build())
{
var x = container.Resolve<ObservableCollection<A>>();
Console.WriteLine(x.Count);
}
}
}
class A
{
}
class AutofacModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly());
builder.RegisterGeneric(typeof(ObservableCollection<>))
.As(typeof(ObservableCollection<>));
}
}
It seems the issue is cause by:
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly());
If I remove it from AutofacModule, then the output is 0.
Any ideas?
Thanks
Update:
Ah, I think I understand now. Autofac thought I want to resolve all types of A, and there is one type of A in this example (A itself), so the ObservableCollection contains one item. I previously thought only IEnumerable<> has this behavior. But it seems subtypes of IEnumerable<> also have this behavior.
But sometimes what I really want is to inject an collection, for example, sometime I need to inject DispacherNotifiedObservableCollection into my ViewModels. Any workarounds?
Update 2:
Based on the answer of Nicholas Blumhardt, I changed my code to:
builder.RegisterGeneric(typeof(ExtendedObservableCollection<>))
.As(typeof(IObservableCollection<>))
.UsingConstructor();
public interface IObservableCollection<T> :
IList<T>, ICollection<T>, IEnumerable<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
void AddRange(IEnumerable<T> list);
void Sort<TKey>(Func<T, TKey> keySelector, System.ComponentModel.ListSortDirection direction);
void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer);
}
Now everything works fine. Thanks!
The behavior you're seeing is a result of the ObservableCollection type having a constructor that accepts IEnumerable.
You can change this to use the default constructor using the UsingConstructor() option.
ObservableCollection itself might not be a very good contract to depend on though- it is a bit unclear what the semantics should generally be. Wrapping it in a specialized component with it's own interface is the better option.

Entity Framework and Entity Tracker Problems

If I run the following code it throws the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
public void Save(Category category)
{
using(var db = new NorthwindContext())
{
if(category.CategoryID == 0)
{
db.AddToCategorySet(category);
}
else
{
//category.RemoveTracker();
db.Attach(category);
}
db.SaveChanges();
}
}
The reason is of course that the category is sent from interface which we got from GetById method which already attached the EntityChangeTracker to the category object. I also tried to set the entity tracker to null but it did not update the category object.
protected void Btn_Update_Category_Click(object sender, EventArgs e)
{
_categoryRepository = new CategoryRepository();
int categoryId = Int32.Parse(txtCategoryId.Text);
var category = _categoryRepository.GetById(categoryId);
category.CategoryName = txtUpdateCategoryName.Text;
_categoryRepository.Save(category);
}
I'm still learning Entity Framework myself, but maybe I can help a little. When working with the Entity Framework, you need to be aware of how you're handling different contexts. It looks like you're trying to localize your context as much as possible by saying:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... within your data access method. Did you do the same thing in your GetById method? If so, did you remember to detach the object you got back so that it could be attached later in a different context?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
That way when you call Attach it isn't trying to step on an already-attached context. Does that help?
As you pointed out in your comment, this poses a problem when you're trying to modify an item and then tell your database layer to save it, because once an item is detached from its context, it no longer keeps track of the changes that were made to it. There are a few ways I can think of to get around this problem, none of them perfect.
If your architecture supports it, you could expand the scope of your context enough that your Save method could use the same context that your GetById method uses. This helps to avoid the whole attach/detach problem entirely, but it might push your data layer a little closer to your business logic than you would like.
You can load a new instance of the item out of the new context based on its ID, set all of its properties based on the category that is passed in, and then save it. This costs two database round-trips for what should really only need one, and it isn't very maintainable.
You can dig into the context itself to mark the Category's properties as changed.
For example:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
This looks a little uglier, but should be more performant and maintainable overall. Plus, if you want, you could make it generic enough to throw into an extension method somewhere so you don't have to see or repeat the ugly code, but you still get the functionality out of it.