How query by global secondary index with DynamoDBContext - c#

i have this class with this attributes:
ContactId -> HashKey
Email -> Global Seconday Index Hash Key
CreatedAt -> Property
Actually, i have this method, but throw me an exception because "Email" property is not a HashKey. How can i get an item using a secondary index and DynamoDBContext class? I found some examples, but all of them uses a low-level api.
public Contact Get(string email)
{
Contact entity = null;
using (var context = new DynamoDBContext(new AmazonDynamoDBClient()))
{
entity = context.Load(new Contact() { Email = email });
}
return entity;
}
EDIT: Actually, this code work for me. It assume that "email" is unique:
public Contact Get(string email)
{
Contact entity = null;
using (var context = new DynamoDBContext(new AmazonDynamoDBClient()))
{
entity = context.Query<Contact>(email, new DynamoDBOperationConfig {IndexName = "Email-index"}).FirstOrDefault();
}
return entity;
}

If using .NET core or .NET 5 and higher with async API calls, the following should work. Note the QueryAsync method and GetRemainingAsync().Result which returns the result of the completed query task.
public Contact Get(string email)
{
Contact entity = null;
using (var context = new DynamoDBContext(new AmazonDynamoDBClient()))
{
entity = context.QueryAsync<Contact>(email,
new DynamoDBOperationConfig {IndexName = "Email-index"})
.GetRemainingAsync().Result.FirstOrDefault();
}
return entity;
}

Related

EF Core with Sqlite Memory Database for testing throws "Collection is read-only" error - why?

I'm writing integration test with ef core using sqlite memory database. Here is the code:
public async Task GetCustomerAndRidesById_When_MultipleCustomersArePresent()
{
var connectionStringBuilder =
new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connection = new SqliteConnection(connectionStringBuilder.ToString());
var options = new DbContextOptionsBuilder<TravelContext>()
.UseSqlite(connection)
.Options;
var customer = _customerBuilder.WithDefaults();
Customer customerFromRepo;
using (var context = new TravelContext(options))
{
context.Database.OpenConnection();
context.Database.EnsureCreated();
await context.Customers.AddAsync(customer);
await context.SaveChangesAsync();
_output.WriteLine($"Customer ID: {customer.Id}");
//var customerRepository = new CustomerRepository(context);
//customerFromRepo = await customerRepository.GetByIdWithRidesAsync(customer.Id);
}
using (var context = new TravelContext(options))
{
var customerRepository = new CustomerRepository(context);
try
{
customerFromRepo = await customerRepository.GetByIdWithRidesAsync(customer.Id);
}
catch (System.Exception e)
{
throw;
}
}
customerFromRepo.Should().BeEquivalentTo(customer);
customerFromRepo.Rides.Should().HaveCount(customer.Rides.Count);
}
The above code throws following error
Collection is read-only
error. However if I comment out the second using block and uncomment the lines inside first using block, records are retrieved and test passes.
Here is my Customer class:
public class Customer : BaseEntity<Guid>, IAggregateRoot
{
private Customer()
{
// required by EF
}
public Customer(string name, List<Ride> rides)
{
Name = name;
_rides = rides;
}
public string Name { get; set; }
private readonly List<Ride> _rides = new List<Ride>();
public IReadOnlyCollection<Ride> Rides => _rides.AsReadOnly();
}
I'm puzzled. Can anyone explain why?
Thanks
Enabling the below configuration fixes the issue.
var navigation = builder.Metadata.FindNavigation(nameof(Customer.Rides));
navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
Credits and Thanks to Ivan Stoev for guidance.

Entity Framework 6 - update fails (disconnected scenario)

I'm trying to update an instance with Entity Framework 6. I suppose it's a disconnected scenario. And the update fails - no errors but the properties I change do not save in DB.
Method in controller
var managers = _iManagerRepository.Managers.ToList();
var manager = managers.FirstOrDefault(m => m.Id == currentUserId);
if (manager != null)
{
manager.ContactDetail.FirstName = withTokenDto.managerInfoModel.FirstName;
manager.ContactDetail.SecondName = withTokenDto.managerInfoModel.SecondName;
manager.ContactDetail.LastName = withTokenDto.managerInfoModel.LastName;
_iManagerRepository.UpdateManager(manager);
return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK));
}
Method in repository:
public void UpdateManager(Manager manager)
{
using (LightCRMEntities context = new LightCRMEntities())
{
context.Managers.Attach(manager);
context.Entry<Manager>(manager).State = EntityState.Modified;
context.SaveChanges();
}
}

How do I use async Task<IActionResult> ? Or How to run in async way in my Asp.Net Core Web Api

I'am trying to run my Controller Action in async way.
How do I use async Task ? Or How to run in async way
// Db context
public class DeptContext : DbContext
{
public LagerContext(DbContextOptions<LagerContext> options)
: base(options)
{
Database.Migrate();
}
public DbSet<Department> Departments { get; set; }
public DbSet<Product> Products { get; set; }
}
// This is my Interface IDepRepository
Task<Department> GetDepartmentWithOrWithoutProducts(int deptId, bool includeProducts);
// And my Repository class DepRepository
public class DepRepository : IDepRepository
{
private DeptContext db;
public DepRepository(DeptContext context)
{
db = context;
}
// I'am geting Department name with products or Without products
public async Task<Department> GetDepartmentWithOrWithoutProducts(int deptId, bool includeProducts)
{
if(includeProductss)
{
return await db.Departments.Include(c => c.Products).Where(s => s.deptId == deptId).SingleAsync();
}
return await db.Departments.Where(s => s.deptId == deptId).SingleAsync();
}
}
So How should I do now in my Controller to do it as async way: I tried as following but I don't know if it's right to do like this following:
I'm not getting any error but I don't if it's right way ...
using System.Threading.Tasks;
using System.Net;
using Microsoft.Data.Entity;
using Microsoft.EntityFrameworkCore;
[Route("api/departments")]
public class DepartmentsController : Controller
{
private IDeptRepository _deptInfoRepository;
public DepartmentsController(IDeptRepository deptInfoRepository)
{
_deptInfoRepository = deptInfoRepository;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetDepatment(int id, bool includeProducts = false)
{
var dept = _deptInfoRepository.GetDepartmentWithOrWithoutProducts(id, includeComputers);
if(dept == null)
{
return BadRequest();
}
if(includeProducts)
{
var depResult = new DepartmentDto() { deptId = dept.deptId, deptName = dept.deptName };
foreach(var department in dept.Products)
{
depResult.Products.Add(new ProductDto() { productId = department.productId, deptId = department.deptId, ProductName = department.ProductName });
}
return Ok(depResult);
}
var departmentWithoutProductResult = new DepartmentsWithoutProductsDto() { DeptId = dept.deptId, DeptName = dept.DeptName};
return Ok(departmentWithoutProductResult);
}
How do I do to get my controller in async way.. I don't know where to put those await and ToListAsync(). Thank you in advance!
The interface should be renamed to better show the intent.
public interface IDepRepository {
Task<Department> GetDepartmentWithOrWithoutProductsAsync(int deptId, bool includeProducts);
//...
}
Which would update the implementation accordingly. Since the method is not actually using anything after the async call then there not really any reason to tag the method as async. Just return the Task.
public Task<Department> GetDepartmentWithOrWithoutProductsAsync(int deptId, bool includeProducts) {
if(includeProductss) {
return db.Departments.Include(c => c.Products).Where(s => s.deptId == deptId).SingleAsync();
}
return db.Departments.Where(s => s.deptId == deptId).SingleAsync();
}
The controller action however needs to await the task and then continue after the task has completed so therefore that method will be tagged with async.
[HttpGet("{id}")]
public async Task<IActionResult> GetDepatment(int id, bool includeProducts = false) {
var dept = await _deptInfoRepository.GetDepartmentWithOrWithoutProductsAsync(id, includeComputers);
if (dept == null) {
return BadRequest();
}
if (includeProducts) {
var depResult = new DepartmentDto() { deptId = dept.deptId, deptName = dept.deptName };
foreach (var department in dept.Products) {
depResult.Products.Add(new ProductDto() {
productId = department.productId,
deptId = department.deptId,
ProductName = department.ProductName
});
}
return Ok(depResult);
}
var departmentWithoutProductResult = new DepartmentsWithoutProductsDto() { DeptId = dept.deptId, DeptName = dept.DeptName};
return Ok(departmentWithoutProductResult);
}
What I can't tell from your code is the datatype of what GetDepartments returns. My guess is that you are using EF Core and GetDepartments returns a DbSet or a LINQ query against a DbSet. If that is the case, then after the line where your depEntities variable is set, that variable points to a deferred object (an expression tree that has not been evaulated yet). Or in other words, the actual query has not been sent to the database yet. When you loop over the depEntities (with your foreach loop), you are causing the actual potentially long-running work to occur (database access). That's what you want to await on. So, yes, you could make an async version of GetDepartments or you could also probably change your code to be:
var depEntities = await _depRepository.GetDepartments().ToListAsync();
The call to ToListAsync will enumerate the deferred object and perform the database access. Your return statement would just return results. Behind the scenes, the method actually returns on your await statement and resumes after the work you're awaiting on completes.
One last note.. any database exceptions will occur at the point where the deferred object is enumerated.
You should not do any await on already-prepared results list. It's already contain required data - what you want to wait to?
You should make new async version of your GetDepartments() method and await while obtaining data from repository:
var depEntities = await _depRepository.GetDepartmentsAsync();

Update all fields of an object using entity framework

I want to change all of an object properties using entity framwork.
after searching i got to have this:
Controller,action:
public ActionResult Test()
{
var user = GetCurrentUser();
user.FirstName = "BLAH BLAH";
new UserRepository().UpdateUser(user);
return RedirectToAction("Index");
}
and in my UserRepository:
public bool UpdateUser(ApplicationUser target)
{
using (var db = new AppDatabase())
{
db.Entry(target).State = EntityState.Modified;
db.SaveChanges();
return true;
}
}
but when i try execute i got this error
An entity object cannot be referenced by multiple instances of EntityChangeTracker.
so,any ways to fix or any better way?
using entity framework 6.0.0 and .net 4.5
public ApplicationUser GetCurrentUser()
{
return UserManager.FindById(User.Identity.GetUserId());
}
You should use same instance of db context for finding and updating, so you UserRepository can be:
class UserRepository : IDisposable //using IDisposable to dispose db context
{
private AppDatabase _context;
public UserRepository()
{
_context = new AppDatabase();
}
public ApplicationUser Find(string id)
{
return _context.Set<ApplicationUser>().Find(id);
}
public void Update(ApplicationUserentity entity)
{
_context.Entry(entity).State = EntityState.Modified;
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
You can use it in controller:
public ActionResult Test()
{
using (var repository = new UserRepository())
{
var user = repository.Find(User.Identity.GetUserId());
user.FirstName = "BLAH BLAH";
repository.Update(user);
}
return RedirectToAction("Index");
}
I also think using some dependency injection framework would be beneficial for you. So go for it!!
Be sure that all objects came from the same context!
var userContextOne = new MyDbContext();
var user = userContextOne.Users.FirstOrDefault();
var AppDbContextTwo = new MyDbContext();
// Warning when you work with entity properties here! Be sure that all objects came from the same context!
db.Entry(target).State = EntityState.Modified;
AppDbContextTwo.SaveChanges();
The scond problem (not related to the exception!):
db.Entry(target).State = EntityState.Modified;
Why you are doing that?! You dont not have Detached Scenario? did you have disabled your Changetracker? anyway just execute DetectChanges and this method will find the changed data you do not have to do it by your self.

DbContext has been disposed

I developed a web application with ASP.NET MVC 4 and SQL Server 2008, I create ContextManager class to have only one database context in all pages.
public static class ContextManager
{
public static HotelContext Current
{
get
{
var key = "Hotel_" + HttpContext.Current.GetHashCode().ToString("x")
+ Thread.CurrentContext.ContextID.ToString();
var context = HttpContext.Current.Items[key] as HotelContext;
if (context == null)
{
context = new HotelContext();
HttpContext.Current.Items[key] = context;
}
return context;
}
}
}
It works properly in most of the pages, but in registration page something goes wrong and my context gone deposed with following error:
The operation cannot be completed because the DbContext has been disposed.
public ActionResult Register ( RegisterModel model )
{
if ( ModelState.IsValid )
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount( model.UserName, model.Password,
new
{
Email = model.Email,
IsActive = true,
Contact_Id = Contact.Unknown.Id
} );
//Add Contact for this User.
var contact = new Contact { Firstname = model.FirstName, LastName = model.Lastname };
_db.Contacts.Add( contact );
var user = _db.Users.First( u => u.Username == model.UserName );
user.Contact = contact;
_db.SaveChanges();
WebSecurity.Login( model.UserName, model.Password );
at the line _db.Contacts.Add( contact ); I got the exception.
But without using ContextManager by changing
HotelContext _db = ContextManager.Current;
into:
HotelContext _db = new HotelContext();
the problem was solved. But I need to use my own ContextManager. What is the problem?
Your context has been disposed somewhere else (not in the code you've shown), so basically when you access it from your Register action, it throws the exception.
Actually, you shouldn't use a static singleton to access to your context. Do instantiate a new DbContext instance for each request. See c# working with Entity Framework in a multi threaded server
In my case, my GetAll method was not calling ToList() method after where clause in lambda expression. After using ToList() my problem was solved.
Where(x => x.IsActive).ToList();
You are probably 'lazy-loading' a navigation property of User in your registration view. Make sure you include it by using the Include method on your DbSet before sending it to the view:
_db.Users.Include(u => u.PropertyToInclude);
Also, sharing DbContexts with a static property may have unexpected side effects.
I used to have the same problem. I solved it doing as it was said above. Instantiate a new instance of your context.
Try using this:
using (HotelContextProductStoreDB = new ProductStoreEntities())
{
//your code
}
This way it'll be created a new instance everytime you use your code and your context will not be disposed.
Why override the Dispose(bool)?
public partial class HotelContext : DbContext
{
public bool IsDisposed { get; set; }
protected override void Dispose(bool disposing)
{
IsDisposed = true;
base.Dispose(disposing);
}
}
And, then check IsDisposed
public static class ContextManager
{
public static HotelContext Current
{
get
{
var key = "Hotel_" + HttpContext.Current.GetHashCode().ToString("x")
+ Thread.CurrentContext.ContextID.ToString();
var context = HttpContext.Current.Items[key] as HotelContext;
if (context == null || context.IsDisposed)
{
context = new HotelContext();
HttpContext.Current.Items[key] = context;
}
return context;
}
}
}
Maybe, can be an option.

Categories