I'm setting up a new project and have gotten NHibernate to work with structuremap...sorta. I'm using the NHibernate.Mapping.ByCode.Conformist setup with ClassMaps. No errors occur, but when I query over a session and there are records present in the database for a particular type, nothing comes back. Upon further examination, it appears that the mappings that I've set up for these types are not executing. Here's my code that wires up things for structuremap. I can confirm that it is being executed.
public class OrmRegistry : Registry
{
public OrmRegistry()
{
var sessionFactory = BuildSessionFactory();
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<ISession>().HybridHttpOrThreadLocalScoped().Use(s => sessionFactory.OpenSession());
}
public ISessionFactory BuildSessionFactory()
{
var cfg = new Configuration().DataBaseIntegration(db =>
{
db.ConnectionStringName = "LocalSqlServer";
db.Dialect<MsSql2008Dialect>();
db.Driver<SqlClientDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadUncommitted;
db.BatchSize = 500;
}).AddAssembly(Assembly.GetExecutingAssembly());
if(HttpContext.Current != null)
{
cfg.CurrentSessionContext<WebSessionContext>();
}
else
{
cfg.CurrentSessionContext<ThreadStaticSessionContext>();
}
return cfg.BuildSessionFactory();
}
}
I'm nearly certain I'm just missing something extremely obvious here, but I've been looking at it for a few hours and haven't had any success. I also got downsized a couple days ago, so I don't have a coworker around to look at it.
Looks like you got your configuration initialized, but what about mapping? You need to initialize mappings like this (if you are using conventions):
var mapper = new ConventionModelMapper();
// TODO: define conventions here using mapper instance
// just an example on how I have been using it
var entities = ... // get all entity types here
cfg.AddDeserializedMapping(mapper.CompileMappingFor(entities), "MyMappings");
return cfg.BuildSessionFactory();
And another example if you are using mapping classes (from this post):
var mapper = new ModelMapper();
var mappingTypes = typeof (InvoiceMapping).Assembly.GetExportedTypes()
.Where(t => t.Name.EndsWith("Mapping")).ToArray();
mapper.AddMappings(mappingTypes);
cfg.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
return cfg.BuildSessionFactory();
Related
.Net Core 2.2 / EFC 2.2.3 / Pomelo.EntityFrameworkCore.MySql 2.2.0
Imagine that you have a table called Colors with some predefined data.
public void Configure(EntityTypeBuilder<Color> builder)
{
builder.ToTable("Colors");
builder.HasKey(r => r.Id).UseMySqlIdentityColumn();
builder.Property(r => r.Name).IsRequired().HasMaxLength(255);
builder.Property(v => v.RGB).IsRequired().HasMaxLength(7);
builder.HasData(GetSeed());
}
private ICollection<Color> GetSeed()
{
return new List<Color>()
{
new Color(){Id=1, Name="Black", RGB="#000"},
new Color(){Id=2, Name="White", RGB="#fff"},
}
}
One of my tests is to test the CreateColorCommandHandler. Very straightfoward
var Context = CBERPContextFactory.Create();
var query = new CreateColorCommandHandler(Context);
var command = new CreateColorCommand();
command.Name= "Random color";
command.RGB = "#001122";
var colorId = await query.Handle(command, CancellationToken.None);
//Assert
Assert.IsInstanceOf<long>(colorId);
Assert.NotZero(colorId);
var cor = Context.Colors.Where(p => p.Id == colorId).SingleOrDefault();
Assert.NotNull(cor);
Assert.AreEqual(command.Name, cor.Name);
Assert.AreEqual(command.RGB, cor.RGB);
CBERPContextFactory.Destroy(Context);
//>>> Handle simply add a new entity without informing ID
Handle method
public async Task<long> Handle(CreateColorCommand request, CancellationToken cancellationToken)
{
var entity = new Color
{
Name = request.Name,
RGB = request.RGB,
};
_context.Colors.Add(entity);
await _context.SaveChangesAsync(cancellationToken);
return entity.Id;
}
When I ran this test I get the error An item with the same key has already been added. Key: 1. Which means that InMemoryDatabase do not has auto increment feature.
Am I writing the test wrong?
How can I test case like this? I want to make sure that the command is OK.
Probably I am missing some very basic rule here.
I assume problem is in the following line:
var Context = CBERPContextFactory.Create();
May be you are using the same context instance for multiple tests. According to Testing with InMemory documentation:
Each test method specifies a unique database name, meaning each method has its own InMemory database.
So make sure that your each and every test method has a distinct context instance.
If still does not work then try setting the identity key value manually because InMemory database may does not support auto-increment.
InMemoryDatabase do not have all features yet, and AUTO INCREMENT one of those that need improvements: https://github.com/aspnet/EntityFrameworkCore/issues/6872
Not the answer I wanted, but is the one working for now: clear all seeds before testing.
private static void Clear(this DbContext context)
{
var properties = context.GetType().GetProperties();
foreach (var property in properties)
{
var setType = property.PropertyType;
bool isDbSet = setType.IsGenericType && (typeof(DbSet<>).IsAssignableFrom(setType.GetGenericTypeDefinition()));
if (!isDbSet) continue;
dynamic dbSet = property.GetValue(context, null);
dbSet.RemoveRange(dbSet);
}
context.SaveChanges();
}
I am aware that such question has already been asked, but solution did not help me.
[Fact]
public async Task UpdateAsync()
{
string newTitle = "newTitle1";
int newBrandId = 3;
var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
item.BrandId = newBrandId;
item.Title = newTitle;
storeContext.Entry(item).State = EntityState.Detached;
await service.UpdateAsync(item); // exception inside
var updatedItem = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
Assert.Equal(newTitle, updatedItem.Title);
Assert.Equal(newBrandId, updatedItem.BrandId);
}
public async Task UpdateAsync(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified; // exception when trying to change the state
await _dbContext.SaveChangesAsync();
}
Message: System.InvalidOperationException : The instance of entity type 'Item' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
interesting that exception is the same even if no item retreived from db, like so
//var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
var item = new Item()
{
Id = 1,
BrandId = newBrandId,
CategoryId = 1,
MeasurementUnitId = 1,
StoreId = 1,
Title = newTitle
};
Had the same problem with EF core 2.2. I never experianced this with other applications.
Ended up rewriting all my update functions somehow like this:
public bool Update(Entity entity)
{
try
{
var entry = _context.Entries.First(e=>e.Id == entity.Id);
_context.Entry(entry).CurrentValues.SetValues(entity);
_context.SaveChanges();
return true;
}
catch (Exception e)
{
// handle correct exception
// log error
return false;
}
}
Alexandar's answer, which was to disable tracking completely, solved my issue, but I got worried since I didn't know what this would do to the rest of my application. So I went to the Microsoft docs and found this:
You should not disable change tracking if you want to manipulate entity instances and persist those changes to the database using SaveChanges().
This method sets the default behavior for all contexts created with these options, but you can override this behavior for a context instance using QueryTrackingBehavior or on individual queries using the AsNoTracking(IQueryable) and AsTracking(IQueryable) methods.
So the solution for me was to disable tracking only when needed. So I solved my issue by using this in the other part of my code that retrieved the same entry from the database:
var entry = await context
.SomeDbTable
.AsNoTracking() // this is what you're looking for
.Find(id);
Numerous issues I've been running into have one nasty root.
In a nutshell: I've learned the hard way why dbContext is scoped rather than singleton. Here is Store type, but the issue was the same.
Here is simplified test initialization code
public TestBase()
{
services = new ServiceCollection();
storeContext = StoreContextMock.ConfigureStoreContext(services, output);
serviceProvider = services.BuildServiceProvider();
}
public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
services.AddDbContext<StoreContext>(c =>
c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
var serviceProvider = services.BuildServiceProvider();
var storeContext = serviceProvider.GetRequiredService<StoreContext>();
storeContext .Stores.Add(new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "john#mail.com" });
storeContext .Stores.Add(new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "jennifer#mail.com" });
storeContext .SaveChanges();
return storeContext ;
}
I reread error and finally noticed the main word
The instance of entity type 'Store' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked
So there has to be some orphan tracked instance preventing me from working with store. I did not save any references to s1 or s2, so it must be storeContext storing references on inserted objects even after leaving scope of their declaration and initialization. That's why I was unable update variables normally and also why my 'queried' from db objects had all their navigation properties assigned (lazy loading has little to do with this). The following code resolved all my issues.
public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
services.AddDbContext<StoreContext>(c =>
c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
var serviceProvider = services.BuildServiceProvider();
var storeContext = serviceProvider.GetRequiredService<StoreContext>();
var s1 = new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "john#mail.com" };
var s2 = new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "jennifer#mail.com" }
storeContext .Stores.Add(s1);
storeContext .Stores.Add(s2);
storeContext .Entry<Store>(s1).State = EntityState.Detached;
storeContext .Entry<Store>(s2).State = EntityState.Detached;
storeContext .SaveChanges();
return storeContext ;
}
That is one of many reasons why dbContext should be limited by a scope.
Thanks for the hint.
For me was this the solution:
public void Update(int id, T obj)
{
var entry = table.Find(id);
_context.Entry(entry).CurrentValues.SetValues(obj);
}
Based on the solution Bryan gave. I think I use newer version of EF/Automapping. This works for me.
I got some similar error when I wanted to update data, and I found out I could fix it by clearing the property context. Here is what a did. It's not the same problem but it's the same error, so I think it can be fixed the same way. Clearing the context seems to be a good solution because it's the reason of whats happening.
context.ChangeTracker.Clear();
context.Cliente.Update(cliente);
context.SaveChanges();
I had same problem while I was copying some records in database by Entity Framework and changing one column that was other's entity key.
Tracking mode change did not fix the issue.
The issue was fixed by properly setting primary key in EntityTypeConfiguration, to contain the changed value here described as x.EntityTwoKey.
builder.HasKey(x => new { x.EntityOneKey, x.EntityTwoKey });
In my case I hit this error when running SaveChanges twice inside of two IFs statements. I moved the SaveChanges outside of those two blocks of code. Just a side note in my service layer it is querying the data with AsNoTracking();
if (user.SendPaymentStatus)
{
user.SendPaymentStatus = false;
saveChanges = true;
//_userService.SaveChanges(user, false);
msg = GetPaymentHTML(user.MasterNodeName, user.Payee, DbMasterNode.LastPaidUtc);
Framework.Email.SendEmail(email, "MasterNode Payment - " + user.MasterNodeName, msg);
}
if (user.SendNodeStatus)
{
user.SendNodeStatus = false;
saveChanges = true;
//_userService.SaveChanges(user, false);
msg = GetStatusHTML(user.MasterNodeName, user.Payee, DbMasterNode.CurrentStatus, DbMasterNode.LastSeenUtc);
Framework.Email.SendEmail(email, "MasterNode Down - " + user.MasterNodeName, msg);
}
if (saveChanges)
{
user.SendPaymentStatus = false;
_userService.SaveChanges(user, false);
}
I was getting the same problem when was trying to update the value. then i found the proble i was using this.
services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("databasename")),ServiceLifetime.Singleton);
then i remove lifetime and it worked well for me.
services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("databasename")));
In my case above issue was resolved after I set primary key column Id as an Identity column.
We recently run into the same issue when adding multiple new items with identity column id set to 0. We are using OracleDataAccess client for EF core 3, we set the sequence number for the new entities when we do saveChanges(), but it errors out when we try to add() if there's already another item with id=0.
The fix we did is making sure the configuration for the identity column is correct:
1.) Set the key
builder.HasKey(t => t.Id);
2.) Set the database generate option correctly
[Column("ID"), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
or fluent equivalent:
builder.Property(t => t.Id)
.ValueGeneratedOnAdd();
We did not do second step correctly and was setting as DatabaseGeneratedOption.None, then EF core failed on add.
public class MyService
{
private readonly ISomething _something;
private readonly Func<IRarelyGetUsed> _rarelyGetUsed;
public MyService(ISomething something, Func<IRarelyGetUsed> rarelyGetUsed)
{
_something = something;
_rarelyGetUsed = rarelyGetUsed;
}
}
We use Autofac for our IOC and found we can get big performance gains (when under load) using the Func<T> approach because those dependencies don't get resolved until they are used, and in some scenarios certain dependencies are not used.
We are also using Moq for some unit testing.
var _container = new AutoMocker();
var _service = _container.CreateInstance<MyService>();
At this point it blows up - System.NullReferenceException : Object reference not set to an instance of an object.
Anyone know how to tell Moq to play nicely with Func dependencies?
Note that if I change Func<IRarelyGetUsed> to IRarelyGetUsed there's no exception.
Edit: Turns out the nuget package was pretty old - after updating the package https://github.com/tkellogg/Moq.AutoMocker this is now working.
However, there's one more problem to solve -
_container.GetMock<Func<IRarelyGetUsed>>().Setup(p => p().DoSomething(It.IsAny<string>())).Returns(true).Verifiable();
Trying to setup the result of the above method result in - Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.InvocationExpression'
Edit 2:
var serviceMock = _container.GetMock<IRarelyGetUsed>();
serviceMock.Setup(r => r.DoSomething()).Returns(someData);
_container.GetMock<Func<IRarelyGetUsed>>().Setup(s => s()).Returns(serviceMock.Object);
The above now works, however it requires setting up both the Func<IRarelyGetUsed> and IRarelyGetUsed - would be nice if it was only necessary to do one, otherwise there's more overhead per test.
You can automatically wire up a Func<T> for every T with AutoMocker doing something like this:
public void RegisterFuncs(AutoMocker autoMocker, IEnumerable<Type> types)
{
var use = typeof(AutoMocker).GetMethods()
.First(t => t.Name == "Use" &&
t.GetGenericArguments().First().Name == "TService");
var get = typeof(AutoMocker).GetMethod("Get");
foreach (var type in types)
{
// _.container.Use<Func<T>>()
var typedUse = use.MakeGenericMethod(typeof(Func<>).MakeGenericType(type));
// _container.Get<T>()
var typedGet = get.MakeGenericMethod(type);
var target = Expression.Constant(autoMocker);
var call = Expression.Call(target, typedGet);
// () => _container.Get<T>()
var lambda = Expression.Lambda(call);
// _.container.Use<Func<T>>(() => _container.Get<T>())
typedUse.Invoke(autoMocker, new object[] { lambda.Compile() });
}
}
// Then call with your AutoMocker instance and the interfaces you want to wire up
var types = typeof(SomeNamespace.ISomeInterface).Assembly.GetExportedTypes()
.Where(t => t.IsInterface && !t.ContainsGenericParameters);
RegisterFuncs(yourAutoMocker, types);
Run this in your test setup just after creating a container.
Note: to make the above work for Lazy<T>, you have to instantiate the Lazy<T> with a Func<T>, so you'll need something like the following:
public void RegisterLazys(AutoMocker autoMocker, IEnumerable<Type> types)
{
var use = typeof(AutoMocker).GetMethods()
.First(t => t.Name == "Use" &&
t.GetGenericArguments().First().Name == "TService");
var get = typeof(AutoMocker).GetMethod("Get");
foreach (var type in types)
{
// Lazy<T>
var lazyT = typeof(Lazy<>).MakeGenericType(type);
// _.container.Use<Lazy<T>>()
var typedUse = use.MakeGenericMethod(lazyT);
// _container.Get<T>()
var typedGet = get.MakeGenericMethod(type);
var target = Expression.Constant(autoMocker);
var call = Expression.Call(target, typedGet);
// () => _container.Get<T>()
var lambda = Expression.Lambda(call);
// _.container.Use<Lazy<T>>(new Lazy<T>(() => _container.Get<T>()));
typedUse.Invoke(autoMocker, new object[] { Activator.CreateInstance(lazyT, lambda.Compile()) });
}
}
Have you tried using Lazy<T> instead of Func<T> to achieve the lazy loading you desire? It may play better with Moq than Func does.
Documentation on Lazy
I'm running into some issues testing Fluent NHibernate's persistence. I'm not sure if this is simply poor understanding on my part or improper expectations of the test. If so, does anyone have any advice on how best to set up a Unit Test for this part of the DAL?
I have a pair of classes Client and Facility with a one-to-many relationship:
One: Client can have Many Facility
Using this FluentNHibernate's mapping structure, I'd expected they should look like this:
public class ClientMapping : DataMapping<Client>
{
public ClientMapping()
{
HasMany(client => client.Facilities)
.Inverse()
.Cascade
.All();
}
}
public class FacilityMapping : DataMapping<Facility>
{
public FacilityMapping()
{
References(fac => fac.Owner);
}
}
I followed FNH's advice on creating tests such as below but when running it- I get a Client table with 2 Clients and a Facility table with two different Ids, even though I'm passing in a single object.
[Test]
public void CanCorrectlyCreateFacilityTable()
{
_client = new Client {Name = "Preston"};
new PersistenceSpecification<Facility>(session, new DataEqualityComparer())
.CheckProperty(f => f.Id, 1)
.CheckProperty(f => f.Name, _facility1.Name)
.CheckReference(f => f.Owner, _client)
.VerifyTheMappings();
new PersistenceSpecification<Facility>(session, new DataEqualityComparer())
.CheckProperty(f => f.Id, 2)
.CheckProperty(f => f.Name, _facility2.Name)
.CheckReference(f => f.Owner, _client)
.VerifyTheMappings();
}
Closest q/a I've found is those below but even when running the Client test first, I seem to get the same result (likely because the database state resets itself for each test):
Cascade persist creates duplicate rows?
Hibernate - one to Many relationship
It turns out my expectations were incorrect. The Persistence Specification test simply tests where data hits the database - hence, it'll send new items each time it's run.
To test whether the mappings are cascading data correctly I needed to write a test like below:
[Test]
public void CanSaveAndLoadFacilityMapping()
{
object id;
object id2;
using (var trans = _session.BeginTransaction())
{
id = _session.Save(_facility1);
id2 = _session.Save(_facility2);
trans.Commit();
}
_session.Clear();
using (var trans = _session.BeginTransaction())
{
var facility = _session.Get<Facility>(id);
var facility2 = _session.Get<Facility>(id2);
Assert.AreEqual(facility.Name, _facility1.Name);
Assert.AreEqual(facility.Owner.Name, _client.Name);
Assert.AreEqual(facility2.Owner.Name, _client.Name);
Assert.AreEqual(facility.Owner.Id, facility2.Owner.Id);
trans.Dispose();
}
}
I'm trying out Moq and the builder pattern to set up services for testing CRM plugins. On the builder I have a ConfigureMock<T>(Expression<Action<Mock<T>>>) to inject individual configurations. My problem is that once a service is .Setup(), I would like to get the entity the service worked on via .Callback<Entity>(), but I cannot assign it to a local variable in the test, because I can't do assignments in the callback expression.
Here is the config:
private readonly Dictionary<Type, object> MockServices = new Dictionary<Type, object>();
public PluginContextBuilder ConfigMock<T>(Expression<Action<Mock<T>>> setupConfig) where T : class
{
object svc = null;
if (MockServices.TryGetValue(typeof(T), out svc))
{
var mockSvc = (Mock<T>) svc;
Action<Mock<T>> setup = setupConfig.Compile();
setup(mockSvc);
}
return this;
}
And here's an example how I would like to configure in the test (but will not compile):
var createdFanClub = null;
var context = new PluginContextBuilder()
.ConfigMock<IOrganizationService>(c =>
c.Setup(s =>
s.Create(It.IsAny<Entity>()))
.Returns(fanclubGuid))
.Callback<Entity>(a => createdFanClub = a))
If I create an additional Action<Entity> which does the assignment, it works, but I don't think it's practical, in case I have multiple entities, I will need to make just as many assignments for them:
Entity createdFanClub = null;
Action<Entity> assign = a => createdFanClub = a;
var context = new PluginContextBuilder()
.ConfigMock<IOrganizationService>(c =>
c.Setup(s =>
s.Create(It.IsAny<Entity>()))
.Returns(fanclubGuid))
.Callback<Entity>(assign))
Not sure what you try to accomplish. If the goal is to have a builder, which has a handle to all the mocks needed, then why not just return the mock itself, and configure it as one normally will do:
builder.GetMock<IOrganizationService>().Setup....
That will prevent you to chain calls on the same builder (as I see you are trying to do), but it's not a big trade-off, and will simplify your setups.