I am using a GenericRepository pattern when accessing database using EF. I've been using this pattern for years now but it's the first time I have had this issue.
Everywhere in my program, I am able to access the expressions of other repositories implementing the generic one. However, in a particular service I am unable to access the Expression. Here is the code:
GenericRepository.cs
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private readonly DbContext _context;
public DbSet<TEntity> DbSet() => _context.Set<TEntity>();
public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> func, CancellationToken cancellationToken) =>
await DbSet().FirstOrDefaultAsync(func, cancellationToken).ConfigureAwait(false);
}
IGenericRepository
public interface IGenericRepository<TEntity> where TEntity : class
{
Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> func, CancellationToken cancellationToken);
}
AccountRepository (Where it works)
public class AccountRepository : GenericRepository<Account>, IAccountRepository
{
public AccountRepository(DiscountedDbContext context) : base(context)
{ }
}
public interface IAccountRepository : IGenericRepository<Account>
{
}
...
public AccountManagementService(IAccountRepository accountRepository)
{
Account account = await accountRepository.FirstOrDefault(x => x.id == 1);
}
This is how it should look, I.E. showing properties:
And the implementation for the LocationRepository that does not work
public class LocationRecordRepository : GenericRepository<LocationRecord>, ILocationRecordRepository
{
public LocationRecordRepository(DiscountedDbContext context) : base(context)
{
}
}
public interface ILocationRecordRepository : IGenericRepository<LocationRecord>
{
}
...
public GeoLocationService(ILocationRepository locationRepository)
{
// ERROR IS HERE: Properties does not show up for the location repository
LocationRecord current = await locationRepository.FirstOrDefaultAsync(x => x.);
}
This is all that appears when trying to access the model through expressions:
Here are the models:
public class Account
{
public int Id { get; set; }
public string? Email { get; set; }
public string? Name { get; set; }
public string? Surname { get; set; }
public string? Password { get; set; }
public Guid StoreCode { get; set; }
}
public class LocationRecord
{
public int Id { get; set; }
public DateTime? DateRecorded { get; set; }
public string Country { get; set; } = null!;
public string District { get; set; } = null!;
public string Town { get; set; } = null!;
public string PostCode { get; set; } = null!;
public int Count { get; set; } = 0;
}
The problem was simple. I had a Migration called LocationRepository, and it referenced the wrong one.
Related
I am trying to preform a insert action using Entity Framework Core 6.0.11 with identity framework as the base, with Postgres as the database.
I am getting the following error when trying to preform an insert operation:
Serialization and deserialization of 'System.Action' instances are not supported. Path: $.MoveNextAction
This is the code of my insert:
public async Task<CampaignDetialDto> CreateCampaign(BaseCampaignDto campaignDto)
{
var userCampaign = new UserCampaign
{
UserId = UserId,
Campaign = _mapper.Map<Campaign>(campaignDto),
CampaignRole = CampaignRoleEnum.Player
};
await _userCampaignRepo.AddAsync(userCampaign);
return _mapper.Map<CampaignDetialDto>(userCampaign.Campaign);
}
The userCampaignRepo:
public class UserCampaignRepository : GenericRepository<UserCampaign>, IUserCampaignRepository
{
private readonly PostGresContext _context;
private readonly IMapper _mapper;
public UserCampaignRepository(PostGresContext context, IMapper mapper) : base(context, mapper)
{
_context = context;
_mapper = mapper;
}
}
The base repo preforming the action:
public async Task<T> AddAsync(T entity)
{
await _context.AddAsync(entity);
await _context.SaveChangesAsync();
return entity;
}
The context and models:
public class PostGresContext : IdentityDbContext<User>
{
public PostGresContext(DbContextOptions options) : base(options)
{
}
public DbSet<Campaign> Campaigns { get; set; }
public DbSet<UserCampaign> UserCampaigns { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new RoleConfiguration());
modelBuilder.Entity<UserCampaign>().HasKey(x => new
{
x.UserId,
x.CampaignId
});
}
}
public class UserCampaign
{
[Key, Column(Order = 0)]
public string UserId { get; set; }
public virtual User User { get; set; }
[Key, Column(Order = 1)]
public int CampaignId { get; set; }
public virtual Campaign Campaign { get; set; }
public CampaignRoleEnum CampaignRole { get; set; } = CampaignRoleEnum.Player;
}
public class Campaign
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
public ActiveStateEnum Active { get; set; } = ActiveStateEnum.Active;
public virtual ICollection<UserCampaign> UserCampaigns { get; set; }
}
public class User : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<UserCampaign> UserCampaigns { get; set; }
}
public static IServiceCollection AddApplicationPostGresContext(this IServiceCollection services, IConfiguration _config)
{
services.AddDbContext<PostGresContext>(options =>
{
options.UseNpgsql(_config.GetConnectionString("PostGresConnectionString"));
});
return services;
}
I tried altering the add methods hoping it might bring a change but with no effect, I also tried to call the context directly by using _context.addAsync(XXX) or _context.SaveChangesAsync() - this also had the same results.
I would like to add a model into the Postgres database.
For my Project I want to access the database but I have no clue because it is my first time programming with ASP.net mvc.
I have already read through a bunch of guides but to no avail.
Controller
This right here is my controller which gets a Code from a Machine (e.g.: 123456) but when I want to access the database through this option I get the No database provider has been configured for this DbContext. Error Message.
namespace Qualitätskontrolle.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public IActionResult StartPage(string Code)
{
Debug.WriteLine(Code);
ApplicationDbContext dbContext = new ApplicationDbContext(.);
var dbErgebnisse = dbContext.Result.ToList();
for (int i = 0; i < dbErgebnisse.Count; i++)
{
Debug.WriteLine(dbErgebnisse[i]);
}
return View();
}
}
Context Class
I have read that the empty constructor should be removed but then I cannot access it in the Controller class.
namespace Qualitätskontrolle.Data
{
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext()
{
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Bilder> Bilder { get; set; }
public DbSet<Prüfungen> Prüfungen { get; set; }
public DbSet<Ergebnis> Result { get; set; }
public DbSet<Typen> Typen { get; set; }
public DbSet<Typen_Pruefungen_Bilder> Typen_Pruefungen_Bilder { get; set; }
public DbSet<Einstellungen_KoordinatenSys> Einstellungen_KoordinatenSys { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Einstellungen_KoordinatenSys>()
.HasKey(c => new { c.ID, c.BildID });
modelBuilder.Entity<Ergebnis>()
.HasKey(c => new { c.BildID, c.TypenID, c.PruefungenID, c.BauTeilId });
modelBuilder.Entity<Typen_Pruefungen_Bilder>()
.HasKey(c => new { c.PruefungenID, c.TypenID });
}
}
}
Model
This is the model I need. I speficly need the BauTeilId for the Controller Class.
namespace Qualitätskontrolle.Models
{
public class Ergebnis
{
[Key]
public int TypenID { get; set; }
[Key]
public int PruefungenID { get; set; }
[Key]
public int BildID { get; set; }
[Key]
[StringLength(254)]
public string BauTeilId { get; set; }
public DateTime Date { get; set; } = DateTime.Now;
public string XLabel { get; set; }
public int? X { get; set; }
public string YLabel { get; set; }
public int? Y { get; set; }
public string FehlerCode { get; set; }
public string FehlerName { get; set; }
public string FehlerGruppe1 { get; set; }
public string FehlerGruppe2 { get; set; }
public int Result { get; set; }
//1=IO 2=NIO
}
The result should be a list of BauTeilId which I can then check with the Code from the Controller.
If you need further information I will reply quickly.
I'm assume that it's not asp.net mvc core.
You should create separate class which implement DbContext e.g
public class ApplicationCustomDbContext : DbContext
{
public ApplicationCustomDbContext () : base("name=DefaultConnectionCustom")
{
}
// DbSet for your Entities
}
and in web.config you should specific connection string e.g.
<connectionStrings>
<add name="DefaultConnectionCustom" providerName="System.Data.SqlClient" connectionString="___" />
</connectionStrings>
There are multiple issues.
For ApplicationDbContext in .net core, you should register like below in Startup.cs
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString("ApplicationDbContextConnection")));
For connectionstring, you could configure in appsettings.json like
{
"ConnectionStrings": {
"ApplicationDbContextConnection": "Server=(localdb)\\mssqllocaldb;Database=CoreMVC2_2;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
For use, you could resolve from constructure like
public class HomeController : Controller
{
private readonly ApplicationDbContext _context;
public HomeController(ApplicationDbContext context)
{
_mapper = mapper;
_context = context;
_userManager = userManager;
_userStore = userStore;
}
public async Task<IActionResult> Index()
{
var existingStudent = _context.Result.ToList();
return View();
}
}
Below is the my code. I want to get the value of Test.Details & Test.Events
public partial class Test : BaseTypes.ValidationEntityBase
{
public bool Active { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Test()
{
Events = new HashSet<Event>();
}
[Key]
[StringLength(20)]
public string ID { get; set; }
[Required()]
[StringLength(50)]
public string Details { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
[XLTargetName(null)]
public virtual ICollection<Event> Events { get; set; }
public override void AddToModel(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Test>().HasMany(e => e.Events).WithRequired(e => e.Test).WillCascadeOnDelete(false);
}
}
public partial class Events : BaseTypes.ValidationEntityBase
{
public Events()
{
Active = true; //DEFAULT TO ACTIVE
}
[Key()]
public string EventID { get; set; }
[Required()]
[StringLength(20)]
public string ID { get; set; }
[Required()]
[StringLength(50)]
public string EventDetails { get; set; }
[XLTargetName(null)]
[JsonIgnore]
[ScriptIgnore]
public virtual Test Test { get; set; }
public override void AddToModel(System.Data.Entity.DbModelBuilder modelBuilder)
{
}
}
public abstract class ValidationEntityBase : IValidationEntity
{
public ValidationEntityBase()
{
Valid = true;
}
public virtual void Run(CtxTest context, IValidationEntity entity)
{
}
}
public interface IValidationEntity
{
void Run(CtxTest context, IValidationEntity entity);
}
Here is my Business Object
public void RunRules(string typeCode)
{
var inputRules = Rule.FindRules(this.GetContext(), typeCode);
foreach (var rule in inputRules)
{
rule.Run<Test>(this.GetContext(), this.Test, "Sample", typeCode);
}
}
My Rule Class:
public void Run<T>(CtxTest context, T entity, string sample, string typeCode) where T : class, IValidationEntity
{
var validation = this.GetExecutionType();
var execution = (IValidationEntity)Activator.CreateInstance(validation, sample);
execution.Run(context, entity);
}
Whenever running rule then It will come to the below class and I'm getting all the basetypes(Test class) value
public class Person : ValidationEntityBase
{
public Person(string msgTypeCode)
{
MESSAGE_TYPECODE = msgTypeCode;
}
public override void Run(Context.CtxTest context, IValidationEntity entity)
{
}
}
How to print the value of Test.Details & Test.Events from IValidationEntity entity in run method, please help
This seems to be the simplest answer:
public interface IValidationEntity
{
void Run(CtxTest context, IValidationEntity entity);
string Details { get; }
}
It will require implementing explicitly to allow your classes to have different implementation names (i.e. string EventDetails) while still conforming to the IValidationEntity interface.
I'm using EF Core 2.1 and I have these class in my Domain.
public class HomeSection2
{
public HomeSection2()
{
HomeSection2Detail = new List<HomeSection2Detail>();
}
public Guid ID { get; set; }
public string Title { get; set; }
public string Header { get; set; }
public List<HomeSection2Detail> HomeSection2Detail { get; set; }
}
public class HomeSection2Detail
{
public Guid ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public int? Sequence { get; set; }
public HomeSection2 HomeSection2 { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.RemovePluralizingTableNameConvention();
//HomeSection2
modelBuilder.Entity<HomeSection2>().HasKey(s => s.ID);
modelBuilder.Entity<HomeSection2>().Property(s => s.ID).ValueGeneratedOnAdd();
modelBuilder.Entity<HomeSection2>().Property(s => s.Title).IsRequired();
modelBuilder.Entity<HomeSection2>().Property(s => s.Header).IsRequired();
//HomeSection2Detail
modelBuilder.Entity<HomeSection2Detail>()
.HasOne(p => p.HomeSection2)
.WithMany(b => b.HomeSection2Detail);
modelBuilder.Entity<HomeSection2Detail>().HasKey(s => s.ID);
modelBuilder.Entity<HomeSection2Detail>().Property(s => s.ID).ValueGeneratedOnAdd();
modelBuilder.Entity<HomeSection2Detail>().Property(s => s.Title).IsRequired();
modelBuilder.Entity<HomeSection2Detail>().Property(s => s.Sequence).IsRequired();
}
And I have a generic repo
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
}
When I call GetAll from the Application var obj = _uow.HomeSection2s.GetAll() like this, it won't fill the Detail.
What you mean is reffered to as 'Lazy Loading'. It would require you to make those properties virtual, like:
public virtual List<HomeSection2Detail> HomeSection2Detail { get; set; }
You can also take a look at this anwser
More documentation on loading related data
I'm using Table per Hierarchy (TPH).
For example we have a base class for all entities:
public abstract class Entity
{
public virtual int Id { get; set; }
public virtual bool IsTransient()
{
return Id == default(int);
}
}
And base class for several entitites:
public abstract class Event:Entity
{
[MaxLength(50)]
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
[MaxLength(100)]
public string ShortDescription { get; set; }
[Required]
public DateTime PublishDate { get; set; }
public int Duration { get; set; }
}
public class Film:Event
{
public string Director { get; set; }
public string ActorList { get; set; }
public override string ToString()
{
return Name;
}
}
public class Concert:Event
{
public string Genre { get; set; }
public override string ToString()
{
return Name;
}
}
My context:
public class MyContext:DbContext
{
public MyContext():base(ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString)
{
}
public DbSet<Event> Events { get; set; }
public virtual void Commit()
{
base.SaveChanges();
}
}
This is base repository:
public class GenericRepository : IRepository
{
//...
public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
{
return GetQuery<TEntity>().AsEnumerable();
}
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
}
private string GetEntityName<TEntity>() where TEntity : class
{
string entitySetName = ((IObjectContextAdapter)DbContext).ObjectContext
.MetadataWorkspace
.GetEntityContainer(((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName, DataSpace.CSpace)
.BaseEntitySets.First(bes => bes.ElementType.Name == typeof(TEntity).Name).Name;
return string.Format("{0}.{1}", ((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName, entitySetName);
}
}
Next, create context and repository:
var context = new MyContext();
EventRepository repository = new EventRepository(context);
var films = repository.GetAll<Film>();
But I get exception (in the GetEntityName method): the sequence does not have elements.
I think it because there are no Film table in the DB. How to solve this problem?
I don't see the need of GetEntityName in the repository you are showing. For GetQuery you can use the DbContext API directly and don't need to access the underlying ObjectContext or MetadataWorkspace:
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
return DbContext.Set<TEntity>();
}
This returns a DbSet<TEntity> (which is an IQueryable<TEntity>). I am not 100% sure if that also works if TEntity is derived but the MSDN documentation about DbSet<TEntity> says: "The type can be derived type as well as base type." So, I would hope that the Set<TEntity>() method is allowed for derived types as well.