I have 4 layer in my application UI,DomainClass,Model(DBCntext),Repository.
In repository i have an abstract class like this :
public abstract class GenericRepository<C, T> :
IGenericRepository<T>
where T : class
where C : DbContext, new()
{
private C _entities = new C();
public C Context
{
get { return _entities; }
set { _entities = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _entities.Set<T>();
return query;
}
public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _entities.Set<T>().Where(predicate);
return query;
}
public virtual void Add(T entity)
{
_entities.Set<T>().Add(entity);
}
public virtual void Delete(T entity)
{
_entities.Set<T>().Remove(entity);
}
public virtual void Edit(T entity)
{
_entities.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
public virtual void Save()
{
_entities.SaveChanges();
}
}
All my entities inheritance from this class like this :
namespace Repository
{
public class StationRepository : GenericRepository<ShirazRailWay.ShirazRailwayEntities, DomainClass.Station>
{
}
}
I UI i called this repositories. as you can see here :
stationrepository objnew=new stationrepository();
obnew.getall();
In UI layer i have an connection string in app.config as you can see here :
<connectionStrings>
<add name="ShirazRailwayEntities" connectionString="metadata=res://*/RailWay.csdl|res://*/RailWay.ssdl|res://*/RailWay.msl;provider=System.Data.SqlClient;provider connection string="data source=****;initial catalog=DB-Metro;user id=sa;password=****;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
</connectionStrings>
But i want to give an option to my users that with this option they can set their connection string by themselves.So i created a form in UI layer that when the users trying to log in it asks them the connection string .My problem is How can pass this connection string to my dbcontext?
In my model layer(dbcontext) i have this :
public partial class ShirazRailwayEntities : DbContext
{
public ShirazRailwayEntities()
: base("name=ShirazRailwayEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Advertisement> Advertisements { get; set; }
public DbSet<Line> Lines { get; set; }
public DbSet<Log> Logs { get; set; }
public DbSet<Path> Paths { get; set; }
public DbSet<Sensor> Sensors { get; set; }
public DbSet<Station> Stations { get; set; }
public DbSet<Train> Trains { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<TimeTable> TimeTables { get; set; }
public DbSet<ConfigFont> ConfigFonts { get; set; }
public DbSet<ArrivalTime> ArrivalTimes { get; set; }
public DbSet<ConfigColor> ConfigColors { get; set; }
}
Add another constructor that takes your connection string:
public partial class ShirazRailwayEntities : DbContext
{
public ShirazRailwayEntities()
: base(name: "ShirazRailwayEntities")
{
}
public ShirazRailwayEntities(string connectionName)
: base(name: connectionName)
{
}
}
var context = new ShirazRailwayEntities("whatever connection name you want");
Related
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.
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 following this example :
Entities
[Table("Authors")]
public class Author {
[Key]
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
[Table("Books")]
public class Book {
[Key]
public int ID { get; set; }
public string Title { get; set; }
public int Author_ID { get; set; }
[ForeignKey("Author_ID")]
public virtual Author Author { get; set; }
}
DbContext
public class MyDbContext : DbContext
{
public virtual DbSet<Author> Authors { get; set; }
public virtual DbSet<Book> Books { get; set; }
public MyDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
}
Generic repository
public interface IRepository<T> where T : class
{
IQueryable<T> Entities { get; }
void Remove(T entity);
void Add(T entity);
}
public class GenericRepository<T> : IRepository<T> where T : class
{
private readonly MyDbContext _dbContext;
private IDbSet<T> _dbSet => _dbContext.Set<T>();
public IQueryable<T> Entities => _dbSet;
public GenericRepository(MyDbContext dbContext)
{
_dbContext = dbContext;
}
public void Remove(T entity)
{
_dbSet.Remove(entity);
}
public void Add(T entity)
{
_dbSet.Add(entity);
}
}
UnitOfWork
public interface IUnitOfWork
{
IRepository<Author> AuthorRepository { get; }
IRepository<Book> BookRepository { get; }
/// <summary>
/// Commits all changes
/// </summary>
void Commit();
/// <summary>
/// Discards all changes that has not been commited
/// </summary>
void RejectChanges();
void Dispose();
}
public class UnitOfWork : IUnitOfWork
{
private readonly MyDbContext _dbContext;
#region Repositories
public IRepository<Author> AuthorRepository =>
new GenericRepository<Author>(_dbContext);
public IRepository<Book> BookRepository =>
new GenericRepository<Book>(_dbContext);
#endregion
public UnitOfWork(MyDbContext dbContext)
{
_dbContext = dbContext;
}
public void Commit()
{
_dbContext.SaveChanges();
}
public void Dispose()
{
_dbContext.Dispose();
}
public void RejectChanges()
{
foreach (var entry in _dbContext.ChangeTracker.Entries()
.Where(e => e.State != EntityState.Unchanged))
{
switch (entry.State)
{
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Modified:
case EntityState.Deleted:
entry.Reload();
break;
}
}
}
}
Question
What if I need a custom Add function?
If I add a Code property in my Book class :
[Table("Books")]
public class Book {
[Key]
public int ID { get; set; }
public string Title { get; set; }
public int Author_ID { get; set; }
public string Code { get; set; } //I'm adding a Code property here
[ForeignKey("Author_ID")]
public virtual Author Author { get; set; }
}
and I want to autofill the Code property before insert the Book object in DB.
I guess I need to create a "custom" BookRepository which inherit the GenericRepository and override the Add function in order to have something like that :
public void Add(Book entity)
{
entity.Code = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds+entity.Title.Replace(" ","");
_dbSet.Add(entity);
}
I'm not familiar with design pattern / inheritance / interface concepts. Is it possible to do something like that?
The logic that you are trying to add belongs the Model and not the Repository, repository has nothing to do with it. Repositories responsible is to access database and run queries against it.
I have written an article how you plan Thin repository and unit of work using EF
I'll insert the link below
https://www.codeproject.com/Articles/1157241/Very-Thin-Database-Layer-using-UnitOfWork-Pattern
Hope this helps
Can we use DbContext without adding EDMX in project for Data model here is sample code in which I am trying to save Instance class object with the help of ContextManager which is DbContext.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects.DataClasses;
using System.Linq;
using System.Text;
namespace DbContext_test
{
public class ContextManager : DbContext
{
public ContextManager(string connstring)
: base(connstring)
{
}
public override int SaveChanges()
{
//TODO: Write code before saving dataEntity object or fire some event to do so.
return base.SaveChanges();
}
}
public class Instances : EntityObject
{
public int ID { get; set; }
public string InstanceCode { get; set; }
public string InstanceName { get; set; }
}
public class InstanceManager
{
readonly string ConnectionString;
public InstanceManager(string connString)
{
ConnectionString = connString;
}
public void SaveInstance(int id, string instanceCode, string instanceName)
{
SaveInstanceInternal(new Instances { ID = id, InstanceCode = instanceCode, InstanceName = instanceName });
}
public void SaveInstance(Instances instance)
{
SaveInstanceInternal(instance);
}
private void SaveInstanceInternal(Instances instance)
{
var contextManager = new ContextManager(ConnectionString);
contextManager.Entry(instance);
contextManager.SaveChanges();
}
}
}
You can do it using the code first approach instead of the edmx approach.
http://www.entityframeworktutorial.net/code-first/what-is-code-first.aspx
follow this
1) create context class
public class SchoolPlusDBContext : DbContext
{
public SchoolPlusDBContext()
: base("name=SchoolPlusDBContext")
{
}
public DbSet<CategoryMaster> CategoryMaster { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
}
2) create class
public class CategoryMaster
{
[Key]
public long CategoryID { get; set; }
[Required]
public string CategoryName { get; set; }
[Required]
public string CategoryType { get; set; }
}
3) DA For query execution
public class CategoryDA
{
SchoolPlusDBContext dbContext = new SchoolPlusDBContext();
public List<CategoryMaster> GetAllCategory()
{
return dbContext.CategoryMaster.OrderByDescending(t => t.CategoryID).ToList();
}
public bool AddCategory(CategoryMaster master,string UserName)
{
try
{
master.CreatedBy = UserName;
master.CreatedOn = System.DateTime.Now;
dbContext.CategoryMaster.Add(master);
dbContext.SaveChanges();
}
catch
{
return false;
}
return true;
}
}
}
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.