[Table("Employee", Schema = "Master")]
public class Employee : Common
{
#region Properties
[Required]
[Key]
public int EmployeeID { get; set; }
public virtual Department Department { get; set; }
public int? DepartmentId { get; set; }
#endregion
}
[Table("Department", Schema = "Lookup")]
public class Department : Common
{
[Required]
[Key]
public int DepartmentId { get; set; }
[Required]
[StringLength(50)]
public string Value { get; set; }
public string Description { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
To get data
var employee = CemexDb.Employee.Where(w => w.EmployeeID == employeeId).FirstOrDefault();
when I fetch data, department always null
Please suggest the workaround
Here my context class that is in my code context class
public class CemexDb : DbContext
{
public virtual IDbSet<T> DbSet<T>() where T : class
{
return Set<T>();
}
public CemexDb() : base(ConfigurationManager.ConnectionStrings["CemexDb"].ConnectionString)
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = true;
}
public CemexDb(string connectionString): base(connectionString)
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = true;
}
public virtual void Commit()
{
base.SaveChanges();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
Database.SetInitializer<CemexDb>(null);
}
}
here is the access code
public class EmployeeService : RepositoryBase, IEmployeeService
{
public EmployeeService(IDatabaseFactory DbFactory): base(DbFactory)
{ }
}
Repository base class
public abstract class RepositoryBase
{
private CemexDb db;
/// <summary>
/// Holds a reference to the DatabaseFactory class used to manage connections to the database.
/// </summary>
protected IDatabaseFactory DatabaseFactory { get; private set; }
/// <summary>
/// Contains a reference to the <see cref="System.Data.Entity.DbContext"/> instance used by the repository.
/// </summary>
protected CemexDb CemexDb { get { return db ?? (db = DatabaseFactory.Get()); } }
/// <summary>
/// Initialises a new instance of the RepositoryBase class.
/// </summary>
/// <param name="DbFactory">A valid DatabaseFactory <see cref="Opendesk.Data.DatabaseFactory"/> object.</param>
public RepositoryBase(IDatabaseFactory DbFactory)
{
DatabaseFactory = DbFactory;
}
}
You must have proxy generation enabled and collection properties defined as virtual in order to have lazy loading working.
Also context should stay alive.
public class CemexDb : DbContext
{
public CemexDb()
{
this.Configuration.ProxyCreationEnabled = true;
}
public DbSet<Unicorn> Employees { get; set; }
}
Related
I have this simple data model of some reservations and theirs cancellations:
[Table("ReservationCreation")]
public class ReservationCreation
{
[Key()]
public int ReservationCreationId { get; set; }
[InverseProperty("ReservationCreation")]
public virtual ReservationCancellation ReservationCancellation { get; set; }
}
[Table("ReservationCancellation")]
public class ReservationCancellation
{
[Key()]
[ForeignKey("ReservationCreation")]
public int ReservationCancellationId { get; set; }
[Required]
[ForeignKey("ReservationCancellationId")]
[InverseProperty("ReservationCancellation")]
public virtual ReservationCreation ReservationCreation { get; set; }
}
public class DbContext : System.Data.Entity.DbContext
{
public DbContext() : base(#"DefaultConnection") { }
public DbSet<ReservationCancellation> ReservationCancellation { get; set; }
public DbSet<ReservationCreation> ReservationCreation { get; set; }
}
internal sealed class Configuration : DbMigrationsConfiguration<DbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
}
Here is the code of the test. First the reservation is created and then it is cancelled.
When the cancellation record is being saved into database then an exception is thrown "The ReservationCreation field is required".
How can I create cancellation record only from the reservation's ID and at the same time have the navigation properties defined?
class Program
{
static void Main(string[] args)
{
int reservationId;
// create reservation
using (var db = new DbContext())
{
var reservation =
db.ReservationCreation.Add(
new ReservationCreation());
db.SaveChanges();
reservationId = reservation.ReservationCreationId;
}
// cancel reservation by its Id
using (var db = new DbContext())
{
var cancellation =
db.ReservationCancellation.Add(
new ReservationCancellation
{
ReservationCancellationId = reservationId
});
try
{
// an exception is thrown
db.SaveChanges();
}
catch(DbEntityValidationException ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
foreach (var err in ex.EntityValidationErrors.SelectMany(x_ => x_.ValidationErrors))
System.Diagnostics.Debug.WriteLine($"!!!ERROR!!! {err.PropertyName}: {err.ErrorMessage}");
}
}
}
}
I did not find any way how to modify the data model annotations. If I remove [Required] from ReservationCreation property then I am not able to create the migration {or connect to the database with that data model).
Your mixing things up in your ReservationCancellation model.
In your ReservationCreation property you are referring to the primary key entity instead of the ReservationCreation property.
Try this.
[Table("ReservationCancellation")]
public class ReservationCancellation
{
[Key()]
public int ReservationCancellationId { get; set; }
[ForeignKey("ReservationCreation")]
public int ReservationCreationId { get; set; }
[Required]
public virtual ReservationCreation ReservationCreation { get; set; }
}
Update
Since you want only one cancellation per creation, you can do this using a simpler model.
[Table("ReservationCreation")]
public class ReservationCreation
{
[Key()]
public int ReservationCreationId { get; set; }
public virtual ReservationCancellation ReservationCancellation { get; set; }
}
[Table("ReservationCancellation")]
public class ReservationCancellation
{
[Key()]
public int ReservationCancellationId { get; set; }
public virtual ReservationCreation ReservationCreation { get; set; }
}
I followed the recommendations from #dknaack and my final solution of this problem is this data model:
[Table("ReservationCreation")]
public class ReservationCreation
{
[Key()]
public int ReservationCreationId { get; set; }
[InverseProperty("ReservationCreation")]
public virtual ReservationCancellation ReservationCancellation { get; set; }
}
[Table("ReservationCancellation")]
public class ReservationCancellation
{
[Key()]
[ForeignKey("ReservationCreation")]
public int ReservationCancellationId { get; set; }
[ForeignKey("ReservationCancellationId")]
public virtual ReservationCreation ReservationCreation { get; set; }
}
Hi I am trying to build angular 2 web application using WebAPI, Entityframework that is loosely coupled using dependency injection. I am using unity for dependency injection. I have created multiple projects in one solution to address the separation concerns.
I have configured the dependency in unity.config however when i execute the webapi application and type the following url http://localhost:8702/api/allcustomers , I get message saying the customer controller doesn't have parameter-less constructor. I have set my break points in unity.config which never get hit
I would like to to understand if my implementation is correct as well
Below is the structure of my solution
CustomerOrder.Business.Objects
CustomerOrder.Data.Objects (references the business object)
CustomerOrder.Service.Api (references business object and service implementation)
CustomerOrder.Service.Implementation (references business objects and data objects)
CustomerOrder.Web (Yet to implement)
Below is the code
CustomerOrder.Business.Objects
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public int? Zip { get; set; }
}
CustomerOrder.Data.Objects
public class CustomerDao : ICustomerDao
{
IEnumerable<CustomerOrder.BusinessObjects.Customer> ICustomerDao.GetAllCustomers()
{
using (var customerOrderContext = new Entities())
{
return (from customer in customerOrderContext.Customers
select new CustomerOrder.BusinessObjects.Customer
{
Id = customer.Id,
FirstName = customer.FirstName,
LastName = customer.LastName,
Address = customer.Address,
City = customer.City,
Email = customer.Email,
Gender = customer.Gender,
State = customer.State,
Zip = customer.Zip
}).ToList();
}
}
}
public interface ICustomerDao
{
/// <summary>
/// Get All Customers
/// </summary>
/// <returns></returns>
IEnumerable<Customer> GetAllCustomers();
}
public interface IDaoFactory
{
ICustomerDao CustomerDao { get; }
}
}
public class DaoFactory : IDaoFactory
{
public DaoFactory(ICustomerDao CustomerDao, IProductDao ProductDao, IOrderDao OrderDao)
{
this.CustomerDao = CustomerDao;
}
public ICustomerDao CustomerDao { set; get; }
}
CustomerOrder.Service.Api
Unity.Config
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterType<ICustomerProvider, CustomerProvider>();
container.RegisterType<IOrderProvider, OrderProvider>();
container.RegisterType<IProductProvider, ProductProvider>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
CustomerController.cs
public class CustomerController : ApiController
{
private ICustomerProvider customerProvider;
public CustomerController(ICustomerProvider customerProvider)
{
this.customerProvider = customerProvider;
}
[Route("api/allcustomers")]
public IEnumerable<Customer> GetAllCustomers()
{
return customerProvider.GetAllCustomers();
}
CustomerOrder.Service.Implementation
public interface ICustomerProvider
{
IEnumerable<BusinessObjects.Customer> GetAllCustomers();
}
public class CustomerProvider : ICustomerProvider
{
private readonly IDaoFactory dataAccess;
public CustomerProvider(IDaoFactory dalFactory)
{
this.dataAccess = dalFactory;
}
public IEnumerable<BusinessObjects.Customer> GetAllCustomers()
{
IList<BusinessObjects.Customer> customerCollection = new List<BusinessObjects.Customer>();
dataAccess.CustomerDao.GetAllCustomers();
return customerCollection;
}
}
Context Class
namespace CustomerOrderData.EF
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class Entities : DbContext
{
public Entities()
: base("name=Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Customer> Customers { get; set; }
public virtual DbSet<OrderDetail> OrderDetails { get; set; }
public virtual DbSet<Order> Orders { get; set; }
public virtual DbSet<Product> Products { get; set; }
}
}
In CustomerProvider, the IDaoFactory is probably not getting resolved because it's not registered. Add this to the Unity.Config:
container.RegisterType<IDaoFactory , DaoFactory >();
Please try including a parameterless constructor into the customer controller.
public CustomerController() {}
You should register not only IDaoFactory and his constructor dependencies
container.RegisterType<IDaoFactory, DaoFactory>();
container.RegisterType<ICustomerDao, CustomerDao>();
container.RegisterType<IOrderDao, OrderDao>();
container.RegisterType<IProductDao, ProductDao>();
I'm trying to find an easy solution for updating an entity + the included properties in my solution. I've created an Generic Repository for my DBContext (database). It does update the parent entity, but not handling changes on the child properties. Is there a way to handle or track those changes?
Example code for updating child propery: (look at comment - example code)
[HttpPut]
public HttpResponseMessage PutBrand(Brand brand)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
try
{
// example code
brand.BrandSizes.FirstOrDefault().Name = "I'm a Test";
// add values
brand.State = State.Changed;
brand.DateChanged = DateTime.Now;
// update
brand = _brandService.UpdateBrand(brand);
// save
_brandService.SaveBrandChanges();
// signalR
Hub.Clients.All.UpdateBrand(brand);
return Request.CreateResponse<Brand>(HttpStatusCode.OK, brand);
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
Context:
public class ERPContext : DbContext
{
#region Catalog
public DbSet<Brand> Brands { get; set; }
public DbSet<BrandSize> BrandSizes { get; set; }
public DbSet<BrandSizeOption> BrandSizeOptions { get; set; }
public DbSet<BrandTierPrice> BrandTierPrices { get; set; }
#endregion Catalog
public ERPContext()
: base("db-erp")
{
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Generic Repository:
public class ERPRepository<T> : IRepository<T> where T : class
{
#region Fields
private DbSet<T> _dbSet;
private DbContext _dataContext;
#endregion Fields
#region Ctor
public ERPRepository(DbContext dataContext)
{
if (dataContext == null)
{
throw new ArgumentNullException("dataContext", "dataContext cannot be null");
}
_dataContext = dataContext;
_dbSet = _dataContext.Set<T>();
}
#endregion Ctor
#region Methods
public T Add(T item)
{
return _dbSet.Add(item);
}
public T Delete(T item)
{
return _dbSet.Remove(item);
}
public T Update(T item)
{
var updated = _dbSet.Attach(item);
_dataContext.Entry(item).State = EntityState.Modified;
return updated;
}
public IQueryable<T> Query(params Expression<Func<T, object>>[] includes)
{
var query = _dbSet;
if (includes != null)
{
includes.ToList().ForEach(x => query.Include(x).Load());
}
return query;
}
public void SaveChanges()
{
_dataContext.SaveChanges();
}
#endregion Methods
}
Model:
public class Brand
{
#region Ctr
public Brand()
{
BrandSizes = new List<BrandSize>();
BrandTierPrices = new List<BrandTierPrice>();
}
#endregion Ctr
#region Properties
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? LogoId { get; set; }
public int DisplayOrder { get; set; }
public bool Deleted { get; set; }
public bool Locked { get; set; }
public State State { get; set; }
public DateTime DateChanged { get; set; }
public DateTime DateCreated { get; set; }
#endregion Properties
#region Mapping
public virtual Picture Logo { get; set; }
public virtual List<BrandSize> BrandSizes { get; set; }
public virtual List<BrandTierPrice> BrandTierPrices { get; set; }
#endregion Mapping
}
BrandService:
public partial class BrandService : IBrandService
{
#region Fields
private readonly IRepository<Brand> _brandRepository;
private readonly IRepository<BrandSize> _brandSizeRepository;
private readonly IRepository<BrandSizeOption> _brandSizeOptionRepository;
#endregion Fields
#region Ctor
public BrandService(IRepository<Brand> brandRepository, IRepository<BrandSize> brandSizeRepository, IRepository<BrandSizeOption> brandSizeOptionRepository)
{
_brandRepository = brandRepository;
_brandSizeRepository = brandSizeRepository;
_brandSizeOptionRepository = brandSizeOptionRepository;
}
#endregion Ctor
#region Methods
public virtual IEnumerable<Brand> GetAllBrands()
{
return _brandRepository.Query(x => x.BrandSizes);
//return _brandRepository.Query();
}
public virtual Brand GetBrandById(int id)
{
return _brandRepository.Query().Where(x => x.Id == id).FirstOrDefault();
}
public virtual Brand InsertBrand(Brand brand)
{
return _brandRepository.Add(brand);
}
public virtual Brand UpdateBrand(Brand brand)
{
return _brandRepository.Update(brand);
}
public virtual Brand DeleteBrand(Brand brand)
{
return _brandRepository.Delete(brand);
}
public virtual void SaveBrandChanges()
{
_brandRepository.SaveChanges();
}
#endregion Methods
}
Create IObjectWithState interface and State enum to track changes manually:
public interface IObjectWithState
{
State State { get; set; }
}
public enum State
{
Added,
Unchanged,
Modified,
Deleted
}
and implement the interface in every mapped entity
public class Brand:IObjectWithState
{ ....
[NotMapped]
public State State { get; set; }}
and add these two helper methods to convert the state and to apply the changes in the entire graph:
public static EntityState ConvertState(State state)
{
switch (state)
{
case State.Added :
return EntityState.Added;
case State.Deleted:
return EntityState.Deleted;
case State.Modified:
return EntityState.Modified;
case State.Unchanged:
return EntityState.Unchanged;
default:
return EntityState.Unchanged;
}
}
public static void ApplyStateChanges(this DbContext context)
{
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = StateHelpers.ConvertState(stateInfo.State);
}
}
and when update or insert any object edit the state of it like this object.State = State.Modified;
and then modify your insert or update method to be like this:
public void InsertOrUpdate(T entity, bool IsGraph)
{
if (((IObjectWithState)entity).State == State.Added)
{
dataContext.Entry(entity).State = System.Data.Entity.EntityState.Added;
}
else
{
dbset.Add(entity);
dataContext.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
//This method change the state of every changed object
if (IsGraph)
ApplyStateChanges(dataContext);
dataContext.Commit();
}
This is in reference to the question I asked regarding how to determine when items are added to the virtual ICollection property. As suggested, I have created a custom collection which inherits from Collection as shown below
public class EntityCollection<T> : Collection<T>
{
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
}
}
This is being used as
public class DbAppointment
{
public DbAppointment()
{
exceptionOcurrences = new EntityCollection<DbExceptionOcurrence>();
}
public virtual int AppointmentId { get; set; }
public virtual string Subject { get; set; }
public virtual string Body { get; set; }
public virtual DateTime Start { get; set; }
public virtual DateTime End { get; set; }
private ICollection<DbExceptionOcurrence> exceptionOcurrences;
public virtual ICollection<DbExceptionOcurrence> ExceptionOcurrences
{
get { return exceptionOcurrences; }
set { exceptionOcurrences = value; }
}
}
The problem is the only time the overridden InsertItem method seems to get called is if I initialise the database with a custom initialiser (example code below) and override the seed method!! What am I doing wrong?
Cheers
Abs
public class ContextInitializer : DropCreateDatabaseAlways<Context>
{
protected override void Seed(Context context)
{
new List<DbAppointment>
{
new DbAppointment{ Subject = "hello", Body="world", Start=DateTime.Now, End=DateTime.Now.AddMinutes(30)},
}.ForEach(a => context.Appointments.Add(a));
new List<DbExceptionOcurrence>
{
new DbExceptionOcurrence{ExceptionDate=DateTime.Now}
}.ForEach(eo => context.ExceptionOcurrences.Add(eo));
base.Seed(context);
}
}
please look at the code below.
class Program
{
static void Main(string[] args)
{
using (myContext context = new myContext())
{
Team t = new Team();
t.id = 1;
t.Name = "asd";
context.teamSet.Add(t);
context.SaveChanges();
}
}
}
public abstract class Base
{
public virtual int id { get; set; }
}
public abstract class Player : Base
{
public virtual string Name { get; set; }
public virtual int Number { get; set; }
public virtual Team team { get; set; }
[ForeignKey("team")]
public int teamId { get; set; }
}
public class Team : Base
{
public ICollection<Player> Players { get; set; }
public string Name { get; set; }
}
public class FootballPlayer : Player
{
public double Speed { get; set; }
}
public class BasketballPlayer : Player
{
public double Height { get; set; }
public double Speed { get; set; }
}
public class myContext : DbContext
{
public DbSet<Player> playerSet { get; set; }
public DbSet<Team> teamSet { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BaseConfiguration()).Add(new PlayerConfiguration()).Add(new TeamConfiguration()).Add(new FootballConfiguration()).Add(new BasketballConfiguration());
}
}
public class BaseConfiguration : EntityTypeConfiguration<Base>
{
public BaseConfiguration()
{
HasKey(k => k.id);
Property(p => p.id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
public class PlayerConfiguration : EntityTypeConfiguration<Player>
{
public PlayerConfiguration()
{
Map(p=>{
p.MapInheritedProperties();
p.ToTable("Player");
});
}
}
public class TeamConfiguration : EntityTypeConfiguration<Team>
{
public TeamConfiguration()
{
Map(p =>
{
p.MapInheritedProperties();
p.ToTable("Team");
});
}
}
public class FootballConfiguration : EntityTypeConfiguration<FootballPlayer>
{
public FootballConfiguration()
{
ToTable("FootballPlayer");
}
}
public class BasketballConfiguration : EntityTypeConfiguration<BasketballPlayer>
{
public BasketballConfiguration()
{
ToTable("BasketballPlayer");
}
}
My Player class and Team Class are derived from Based Class, and FootballPlayer and BasketballPlayer are derived from Player. But in the generated database, Player table doesn't contain a FK teamId, it is only a common property. Furthermore, the FootballPlayer and BasketballPlayer tables don't contains the properties which derived from Player class. Anyone can help?
What inheritance mapping are you trying to achieve? At the moment you have TPC between Base and Player and TPT between Player and its derived types. If you want to have inherited properties in those derived types you must use TPC as well but in such case there should be no Player table in your database. To use TPC for player you must use MapInheritedProperties in their mapping configurations.