EF 6 model from database, where do I put the context? - c#

I am using Entity Framework 6. I use it to generate my classes from an existing database. I am adding some fields and methods to these classes using another partial class. My question is about where shall I put the context.
i.e. I created a new constructor which take my context and create a new object and add it to the
context:
public partial class EntityClass1
{
private readonly EntitiesContext dbContext;
private String customString1;
private bool justInitialized;
public EntityClass1(EntitiesContext dbContext)
{
this.dbContext = dbContext;
dbContext.EntityClass1.Add(this);
customString1 = "voila";
justInitialized = true;
}
public MethodUpdateString()
{
customString1 = "UPDATED";
var entity2 = new EntityClass2();
entity2.EntityClass1Id = this.Id;
dbContext.EntityClass2.Add(entity2);
dbContext.SaveChanges();
{
}
public partial class EntityClass2
{
//Some code here
}
public MainClass{
static void Main(){
using (var dbContext = new EntitiesContext())
{
//other operations on the context here
var e1 = new EntityClass1(dbContext);
e1.MethodUpdateString();
//other operations on the context here
}
}
}
I don't why, but I am not really comfortable with this possible implementation. Will it better for example to add a context parameter to the method MethodUpdateString() ? Also because I do a SaveChamges I end the transaction.

Your instincts are correct, both in trying to use constructor injection and in not being comfortable having data access logic inside your model class. An alternative implementation would be this:
public partial class EntityClass1Service
{
private readonly EntitiesContext dbContext;
public EntityClass1Service(EntitiesContext dbContext)
{
this.dbContext = dbContext;
}
public MethodUpdateString(EntityClass1 entityClass1Object)
{
entityClass1Object.CustomString1 = "UPDATED";
dbContext.EntityClass1.Add(entityClass1Object);
var entity2 = new EntityClass2();
entity2.EntityClass1Id = entityClass1Object.Id;
dbContext.EntityClass2.Add(entity2);
dbContext.SaveChanges();
}
}
You might also want to add a try-catch block inside the MethodUpdateString and maybe change customString1 to be a public property.

Related

How to implement expressions in Generic Repository?

I am working on a .Net core project and I have 5 look up tables, so instead of making repository and service for each one I want to make generic repository and generic service for them.
My problem is in implementing FindByID using expressions. I understand the idea behind it but I don't know where or how to use. Any help would be appreciated
Generic Repository Function
private readonly NexusContext _context;
IMapper _mapper;
public GenericRepository(NexusContext context)
{
_context = context;
}
public GenericLookupDTO GetById(Expression<Func<T, bool>> predicate)
{
var obj = _context.Set<T>().Where(predicate);
var objMapped = AutoMapping.mapper.Map<GenericLookupDTO>(obj);
return objMapped;
}
Service Function
private readonly IGenericRepository<T> _genericRepository;
private readonly IUnitOfWork _unitOfWork;
public ConfigurationService(IGenericRepository<T> genericRepository, IUnitOfWork unitOfWork)
{
this._genericRepository = genericRepository;
this._unitOfWork = unitOfWork;
}
List<Errors> errors = new List<Errors>();
try
{
var obj = _genericRepository.GetById(predicate);
if (obj == null)
{
errors.Add(new Errors("404", "Couldn't find configration"));
return new GenericResponse<GenericLookupDTO>(errors, new GenericLookupDTO());
}
await _unitOfWork.CompleteAsync();
return new GenericResponse<GenericLookupDTO>("1", "Success", obj);
}
catch (Exception ex)
{
errors.Add(new Errors(ex.HResult.ToString(), ex.InnerException.Message));
return new GenericResponse<GenericLookupDTO>(errors, new GenericLookupDTO());
}
Controller function
private readonly IConfigurationService<LutNationality> _configurationService;
public NationalityController(IConfigurationService<LutNationality> configurationService)
{
this._configurationService = configurationService;
}
[HttpGet("Id")]
[ProducesResponseType(typeof(GenericLookupDTO), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetByIdAsync()
{
var result = await _configurationService.GetByIdAsync(//what should i pass here);
if (result.operationSuccess)
return Ok(result);
return BadRequest(result);
}
First look up Table
public partial class LutGender
{
public int Id { get; set; }
public string Value { get; set; }
}
Second look up Table
public partial class LutNationality
{
public int Id { get; set; }
public string Value { get; set; }
}
Generic DTO
public class GenericLookupDTO
{
public int Id { get; set; }
public string value { get; set; }
}
The repository pattern is already implemented by the DbSet<T> and consists in few operations over your entity to store and retrive him from an abstracted data store. Just your entity, it's very important on DDD.
But, I know that sometimes we need to put another layer of abstraction over this, to deal with another databases like nosql, for example.
In this case, usually we create a gereneric repository, and it's needed to supply a way to make operations based on what type this repository is. To accomplish this, we need to define a common interface for our entities with an Id and implement this on those entities:
public interface IEntity
{
Guid Id (get; set;}
}
That way, constraining your generic repository to this type of interface provides you ability to access the Id on the methods.
public class GenericRepository<T> : IGenericRepository<T> where T : IEntity
{
private readonly NexusContext _context;
IMapper _mapper;
public GenericRepository(NexusContext context)
{
_context = context;
}
public GenericLookupDTO GetById(Guid id)
{
var obj = _context.Set<T>().FirstOrDefault(x => x.Id = id);
var objMapped = AutoMapping.mapper.Map<GenericLookupDTO>(obj);
return objMapped;
}
}
I really recomend you to don't return DTOs from repository, if you need to aggregate data from many different entities that are not related, use a different layer of data access, very simplified, and create freely your own queries, using dapper or even EF but projecting directly DTOs.
If the DTO is identical of an entity, in this case use the repository to retrieve the entity and on application layer map this entity to a DTO.
When you have time, take a look at DDD principles to clarify a little bit more those subjects.
Back to your example, on the controller you will need to inject the right type of generic repository, like:
IGenericRepository<Customer> customerRepository
and configure your dependecy injection container to resolve generic types, like:
services.AddTransient<IGenericRepository<>, GenericRepository<>>();
The service will rely just on IGenericRepository<T> as you did.
But, if you want to query freely your entities, I recommend you make use of OData or GraphQl, that will provides you more control over queries.
I'm tried to be very simplistic here, so, I hope that i could clarify things a little bit more for you!

MVC Context SaveChanges overwrites database tables

So I've started learning how to use the Entity Framework in MVC using the project template and I have encountered a problem. In my HomeController I wrote the below method GenerateContractorCustomer which when called seems to overwrite the default ASPNetUser tables in the database with the new models I created.
public void GenerateContractorCustomer()
{
using (var context = new ContractorCustomerContext())
{
ContractorModel contractor = new ContractorModel { ContractorID =1, ContractorName ="John"};
context.Contractor.Add(contractor);
context.SaveChanges();
}
}
I created a DbContext:
public class ContractorCustomerContext : DbContext
{
public ContractorCustomerContext() : base("DefaultConnection") { }
public DbSet<ContractorModel> Contractor { get; set; }
public DbSet<CustomerModel> Customer { get; set; }
And in the IdentityModel there is the default:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
My question is what do I need to do to my code so that when I call GenerateContractorCustomer it does not delete the deafult ASPNetUser tables?
You are using an initializer that drops the existing database and creates a new database, if your model classes (entity classes) have been changed. So you don't have to worry about maintaining your database schema, when your model classes change. To change a DB initialization strategy, you could set the DB Initializer using Database class in Context class, as shown below:
public class ExampleDBContext: DbContext
{
public ExampleDBContext(): base("AConnectionString")
{
Database.SetInitializer<ExampleDBContext>(new CreateDatabaseIfNotExists<ExampleDBContext>());
}
}
if you provide a null value you turn off this feature. See the links provided if you want an alternative configuration approach.

Can't find objects which reference entity framework entity

I'm running into an InvalidOperationException because "An entity object cannot be referenced by multiple instances of IEntityChangeTracker." on the first line of EntityFrameWorkRepository.Create().
I know this is due to having multiple database contexts, but in this case I'm a bit lost as the code has no obvious second context since all database access goes through a designated object whose sole purpose is managing database contexts. This was done as the web application in question is fairly interactive and so the user is constantly creating new objects which must be saved in the database. This was causing issues with the previous design, which used locking and a single context, thus the code was refactored and works, except for the method in question.
EF class:
public class EntityFrameWorkRepository<TKey, TEntity> : IDisposable, IRepository<TKey,TEntity> where TEntity: class
{
private readonly IDbContext _context;
private IDbSet<TEntity> _entities;
public EntityFrameWorkRepository()
{
_context = new ApplicationDbContext();
}
private IDbSet<TEntity> Entities
{
get { return _entities ?? (_entities = _context.Set<TEntity>()); }
}
public void Create(TEntity entity)
{
Entities.Add(entity);
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
The service object used for all DB access:
public class Service : IService
{
public const string Persistance = "Persist";
public const int CacheTaskSeconds = 300; //Check every 5 minutes
public const double IdleMinutes = 30.0;
private readonly IKvpRepository<int, SimulationCollection> _simulationCollectionAppStateRepository;
private readonly UserManager<ApplicationUser> _userManager;
public Service(IKvpRepository<int, SimulationCollection> simulationCollectionAppStateRepository)
{
_userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
AddTaskToCache(Persistance, CacheTaskSeconds);
}
public SimulationCollection CreateCollection(Guid userId, string name, string description)
{
using (var _simulationCollectionEFRepository = new EntityFrameWorkRepository<int, SimulationCollectionEntity>())
{
var applicationUser = _userManager.FindById(userId.ToString());
if (applicationUser == null)
throw new ArgumentOutOfRangeException("ApplicationUser matching userId doesn't exist");
var collectionEntity = new SimulationCollectionEntity(applicationUser, name, description);
_simulationCollectionEFRepository.Create(collectionEntity);
return collection;
}
}
}
The object I'm trying to add to the database:
public class SimulationCollectionEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id { get; set; }
public string Name { get; set; }
public virtual ApplicationUser User { get; set; }
public DateTime DateCreated { get; set; }
public string Description { get; set; }
[ForeignKey("SimulationCollectionEntityId")]
public virtual ICollection<SimulationEntity> Simulations { get; set; }
[Obsolete("Only needed for serialization and materialization", true)]
public SimulationCollectionEntity() {}
public SimulationCollectionEntity(ApplicationUser currentUser, string name, string description)
{
User = currentUser;
Name = name;
Description = description;
DateCreated = DateTime.Now;
}
}
Is there an easy way to view what contexts a given object might be attached to? I already checked to see if collectionEntity is attached to _userManager since it has a dbContext, but its state is detached. Does EF maybe expect me to add objects in a different way than I am? I suspect that the attributes in SimulationCollectionEntity might be causing me trouble but I'm new to Entity Framework and I'm not sure. Should I maybe be going for a different design instead like this?
You might want to consider a unit of work like approach where one context is shared among multiple repositories. The accepted answer for this post is a good example. I have seen ContextPerRequest solutions like the one in your example, but I've never been crazy about them. Ideally you want a short lived context that does one thing like add an invoice and two invoice items - a single unit of work. You could then wrap the whole operation in a TransactionScope and have it succeed or fail as a unit.

Exclude related entities even if DbContext has loaded them

Is there a way I can get a list of EntityA without its related navigations, even when DbContext has them. I need this for serialization purposes.
I tried to turn Lazy loading off and explicitly .Include any related entities. But if DbContext has already loaded them, it will be included anyway.
The senario is like this:
public class LookupRepository : ILookupRepository
{
private readonly CustomDbContext _dbContext;
public LookupRepository(CustomDbContext dbContext) {
if(dbContext == null)
throw new ArgumentNullException("dbContext");
_dbContext = dbContext;
}
public IEnumerable<Country> GetCountriesFull() {
return _dbContext.Set<Country>()
.Include(c => c.Areas)
.Include(c => c.Continent)
.ToList();
}
public IEnumerable<Country> GetCountries() {
return _dbContext.Set<Country>()
.ToList();
}
public IEnumerable<Continent> GetContinents() {
return _dbContext.Set<Continent>()
.ToList();
}
public IEnumerable<Area> GetAreas() {
return _dbContext.Set<Area>()
.ToList();
}
}
And the DbContext I inject in there is initialized like this:
public CustomDbContext CreateDbContext(){
var dbContext = new CustomDbContext();
dbContext.Configuration.ProxyCreationEnabled = false;
return dbContext;
}
So this test that uses a clean DbContext passes:
[Test]
public void GetCountries_CalledOnce_ReturnsCountriesWithoutNavigations() {
var sut = CreateLookupRepository();
var countries = sut.GetCountries();
CollectionAssert.IsNotEmpty(countries);
Assert.That(countries.Select(c => c.Continent), Is.All.Null);
Assert.That(countries.Select(c => c.Areas), Is.All.Null);
}
but this one that includes all a call to GetCountriesFull fails:
[Test]
public void GetCountries_AfterCallingGetCountriesFull_StillReturnsNoNavigations() {
var sut = CreateLookupRepository();
var fullCountries = sut.GetCountriesFull();
var countries = sut.GetCountries();
CollectionAssert.IsNotEmpty(countries);
Assert.That(countries.Select(c => c.Continent), Is.All.Null);
Assert.That(countries.Select(c => c.Areas), Is.All.Null);
}
Any advice on how to do this? I thought of using a factory to create a new dbContext for each method (anyway this code is only run on my application startup and data remain in memory as Singleton), but I thought there must me a better solution.
The most simple way to do this is by getting the entities with AsNoTracking(). By doing this, you tell EF not to add the entities to its internal cache and entity relationships will not be resolved.
But here (again) the extra repository layer works against you, because you can't just do a call like
var fullCountries = sut.GetCountriesFull().AsNoTracking();
You have to make overloads, or add parameters like bool withTracking, or initialize the repository with an option to always (or never) use AsNoTracking().
If you don't care that they are being loaded unnecessarily (or they were already filled in for another reason), just set all navigational properties to null if you don't want them to be passed.
You can also add the [XmlIgnore] attribute to your navigational properties, so the serializer will not include them. That also prevents the same issue.
Dear there are many ways to doing it
1)if you are using code 1st then follow this
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.Configuration.LazyLoadingEnabled = false;
}
2)The edmx file has in the and definition an attribute for lazy loading where you can set lazy loading generally to false:
public MyEntitiesContext() : base("name=MyEntitiesContext", "MyEntitiesContext")
{
this.ContextOptions.LazyLoadingEnabled = false;
OnContextCreated();
}
or simply do this
public BlogContext() : base()
{
this.Configuration.LazyLoadingEnabled = false;
}

Linq 2 SQL - A Better way of doing this

I have a repository base class that gets called from my controllers in my mvc application
Each controller gets it own repository
like this
public class ServiceRepository : Bluegrass.Mvc.RepositoryBase<Models.Service>
{
public ServiceRepository()
{
this._db = new iSppms.Models.iSppmsDataContext();
}
}
And gets used like
internal DataAccess.ServiceRepository repository =
new iSppms.DataAccess.ServiceRepository();
public ActionResult Index()
{
var viewData = new ViewData.ServiceIndexViewData(
repository.GetItems<Models.Service>());
return View(viewData);
}
Where I am wanting to do is not have to pass the model through as the RepositoryBase is already of type Models.Service
I am looking for a better way to structure this.
public class RepositoryBase<T> where T : class
{
public DataContext _db;
public List<T> GetItems<T>() where T : class, Interfaces.IModel
{
var table = _db.GetTable<T>();
var data = table.Where(t => !t.Deleted);
return data.ToList();
}
Can you add the IModel bit to RepositoryBase<T>, and make GetItems<T> non-generic? Note: it is already a bad idea to re-use the generic token T (which is different in GetItems):
public class RepositoryBase<T> where T : class, Interfaces.IModel
{
public DataContext _db;
public List<T> GetItems()
{
var table = _db.GetTable<T>();
var data = table.Where(t => !t.Deleted);
return data.ToList();
}
}
Perhaps this Repository Base Class article could help?

Categories

Resources