I have some trouble understanding a JPA behaviour. Mabye someone could give me a hint.
Situation:
Product entity:
#Entity
public class Product implements Serializable {
...
#OneToMany(mappedBy="product", fetch=FetchType.EAGER)
private List<ProductResource> productResources = new ArrayList<ProductResource>();
....
public List<ProductResource> getProductResources() {
return productResources;
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (!(obj instanceof Product)) return false;
Product p = (Product) obj;
return p.productId == productId;
}
}
Resource entity:
#Entity
public class Resource implements Serializable {
...
#OneToMany(mappedBy="resource", fetch=FetchType.EAGER)
private List<ProductResource> productResources = new ArrayList<ProductResource>();
...
public void setProductResource(List<ProductResource> productResource) {
this.productResources = productResource;
}
public List<ProductResource> getProductResources() {
return productResources;
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (!(obj instanceof Resource)) return false;
Resource r = (Resource) obj;
return (long)resourceId==(long)r.resourceId;
}
}
ProductResource Entity:
This is a JoinTable (association class) with additional properties (amount). It maps Product and Resources.
#Entity
public class ProductResource implements Serializable {
...
#JoinColumn(nullable=false, updatable=false)
#ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.PERSIST)
private Product product;
#JoinColumn(nullable=false, updatable=false)
#ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.PERSIST)
private Resource resource;
private int amount;
public void setProduct(Product product) {
this.product = product;
if(!product.getProductResources().contains((this))){
product.getProductResources().add(this);
}
}
public Product getProduct() {
return product;
}
public void setResource(Resource resource) {
this.resource = resource;
if(!resource.getProductResources().contains((this))){
resource.getProductResources().add(this);
}
}
public Resource getResource() {
return resource;
}
...
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (!(obj instanceof ProductResource)) return false;
ProductResource pr = (ProductResource) obj;
return (long)pr.productResourceId == (long)productResourceId;
}
}
This is the Session Bean (running on glassfish).
#Stateless(mappedName="PersistenceManager")
public class PersistenceManagerBean implements PersistenceManager {
#PersistenceContext(unitName = "local_mysql")
private EntityManager em;
public Object create(Object entity) {
em.persist(entity);
return entity;
}
public void delete(Object entity) {
em.remove(em.merge(entity));
}
public Object retrieve(Class entityClass, Long id) {
Object entity = em.find(entityClass, id);
return entity;
}
public void update(Object entity) {
em.merge(entity);
}
}
I call the session Bean from a java client:
public class Start {
public static void main(String[] args) throws NamingException {
PersistenceManager pm = (PersistenceManager) new InitialContext().lookup("java:global/BackITServer/PersistenceManagerBean");
ProductResource pr = new ProductResource();
Product p = new Product();
Resource r = new Resource();
pr.setProduct(p);
pr.setResource(r);
ProductResource pr_stored = (ProductResource) pm.create(pr);
pm.delete(pr_stored);
Product p_ret = (Product) pm.retrieve(Product.class, pr_stored.getProduct().getProductId());
// prints out true ????????????????????????????????????
System.out.println(p_ret.getProductResources().contains(pr_stored));
}
}
So here comes my problem. Why is the ProductResource entity still in the List productResources(see code above). The productResource tuple in the db is gone after the deletion and I do newly retrieve the Product entity. If I understood right every method call of the client happens in a new persistence context, but here i obviously get back the non-refreshed product object!?
Any help is appreciated
Thanks
Marcel
When I use the refresh method in the retrieve method it works.
public Object retrieve(Class entityClass, Long id) {
Object entity = em.find(entityClass, id);
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
em.refresh(entity);
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return entity;
}
So i assume the problem is the caching of eclipselink. If I use the create method on the ProductResource object, the Product and Resource objects are also stored (cascade=CascadeType.Persist). When I load the Product object again it comes out of the cache and therefore is not actual. So I should just also remove the ProductResource object in the lists of the Product and Resource object and do an update. Right? Calling refresh() in this place doesn't really make sense because it bypasses the caching.
Thanks
Marcel
Related
I am creating a project using a realm database.
I also have a lot of lists which I wish to filter while the user types.
The biggest one of them will probably be the list of customers
Currently I am using realm by using a wrapper class for each object (if anyone has a better suggestion I'm definitely open to it)
The objects I am using are like this:
public class Customer_Realm extends RealmObject{
private int _id;
private String _name; //...etc
public int get_id(){ return _id; }
public void set_id(int id) { _id = id;}
//etc
}
Public class Customer() {
Customer_Realm _base;
public Customer(Customer_Realm base) { _base = base;}
public int get_id(){ return _base.get_id(); }
public void set_id(int id) { _base.set_id(id);}
//...etc
}
Along with this I have an ArrayListAdapter for customer objects. My question is, would this:
public class CustomerAdapter extends ArrayListAdapter<Customer> {
List<Customer> _customers;
String _filter = null;
public void set_filter(string name) {
_filter = name;
notifyDataSetChanged();
}
#override
public int getCount(){
return get_visible().count();
}
public Customer getItem(int position){
return get_visible().get(position);
}
public List<Customer> get_visible() {
if (_filter == null)
return _customers;
List<Customer> visible = new ArrayList<Customer> ();
foreach (Customer c : _customers){
if (c.get_name().contains(_filter)
visible.add(c);
}
return visible;
}
}
Or would this be better?
public class CustomerAdapter extends ArrayListAdapter<Customer> {
Realm _database;
String _filter = null;
public void set_filter(string name) {
_filter = name;
notifyDataSetChanged();
}
#override
public int getCount(){
return get_visible().count();
}
public Customer getItem(int position){
return get_visible().get(position);
}
public List<Customer> get_visible() {
RealmResults<Customer_Realm> result = null;
if (_filter == null)
result = _database.allObjects(Customer_Realm.class);
else
result = _database.where(Customer_realm.class).contains("_name",_filter);
List<Customer> visible = new ArrayList<Customer> ();
foreach (Customer_Realm c : result){
visible.add(new Customer(c));
}
return visible;
}
}
Unfortunately my database contains only a small sample of data(and realm for windows does not have an editor so I can enter data manually) so when I try either implementation I do not see any difference, I was wondering if anyone has any experience with what happens when you use such an implementation with a large number of data
Thanks in advance for any help you can provide
The situation
I'm building a web application using the in the title mentioned techniques. This application will something like a CMS system for multiple clients. The client has to login to this system using his company name and login credentials.
With the provided company name, I connect to a database (static DbContext, same connection string every time) where all clients database information is stored and search for this clients specific database(every client has his own with exact same design) login information. That all works fine.
Now here is the tricky part. To continue the login procedure I need to somehow inject or lazy load the repository using the other DbContext with a connection string that is build up from the result of the other database.
What I have
2 DbContexts generated from an existing database, one static and one if possible dynamic.
Then the generic repository classes/interfaces:
public interface IRepository
{
void Submit();
}
public interface IRepository<TEntity, TContext> : IRepository
where TEntity : class
where TContext : DbContext
{
//crud stuff
}
public abstract class GenericRepository<TEntity, TContext> : IRepository<TEntity, TContext>
where TEntity : class
where TContext : DbContext
{
private TContext _dataContext;
private IUnitOfWork _unitOfWork;
private readonly IDbSet<TEntity> dbset;
protected GenericRepository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_unitOfWork.Register(this);
}
}
Unit of work class/interface
public interface IUnitOfWork
{
void Register(IRepository repository);
void Commit();
}
public class UnitOfWork : IUnitOfWork
{
private readonly Dictionary<string, IRepository> _repositories;
private HttpContextBase _httpContext;
public UnitOfWork(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
public void Register(IRepository repository)
{
_repositories.Add(repository.GetType().Name, repository);
}
public void Commit()
{
_repositories.ToList().ForEach(x => x.Value.Submit());
}
}
Then a context/entity specific repository
public class EmployeeRepository : GenericRepository<tbl_Medewerker, CustomerDbEntities>, IEmployeeRepository
{
public EmployeeRepository(IUnitOfWork unitOfWork)
: base(unitOfWork)
{
}
}
public interface IEmployeeRepository : IRepository<tbl_Medewerker, CustomerDbEntities>
{
}
Then the service that implements the repository
public interface IEmployeeLoginService
{
tbl_Medewerker GetEmployeeByLogin(string username, string password);
tbl_Medewerker GetEmployeeByID(Guid id);
}
public class EmployeeLoginService : IEmployeeLoginService
{
private readonly IEmployeeRepository _employeeRepository;
public EmployeeLoginService(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
public tbl_Medewerker GetEmployeeByLogin(string username, string password)
{
return _employeeRepository.Get(e => e.MedewerkerNaam.ToLower() == username.ToLower() && e.Password == password);
}
public tbl_Medewerker GetEmployeeByID(Guid id)
{
return _employeeRepository.GetById(id);
}
}
Finally the controller that implements that service and uses it in the login action
public class AccountController : BaseController
{
IConnectionService _connectionService;
IEmployeeLoginService _employeeService;
public AccountController(IConnectionService connectionService, IEmployeeLoginService employeeService)
{
_connectionService = connectionService;
_employeeService = employeeService;
}
[AllowAnonymous, HttpPost]
public ActionResult Login(LoginModel login)
{
if ((Settings)Session["Settings"] == null)
{
Settings settings = new Settings();
settings.company = _connectionService.GetCompanyName(login.CompanyName);
if (settings.company != null)
{
settings.licence = _connectionService.GetLicenceByCompanyID(settings.company.Company_id);
if (settings.licence != null)
{
settings.connectionStringOrName = string.Format(#"Data Source={0};Initial Catalog={1};User ID={2};Password={3};Application Name=EntityFrameworkMUE", settings.licence.WS_DatabaseServer, settings.licence.WS_DatabaseName, settings.licence.WS_DatabaseUID, settings.licence.WS_DatabasePWD);
Session["Settings"] = settings;
settings.user = _employeeService.GetEmployeeByLogin(login.UserName, login.Password);
if (settings.user != null)
{
FormsAuthentication.SetAuthCookie(string.Format("{0},{1}", settings.company.Company_id.ToString(), settings.user.Medewerker_ID.ToString()) , login.RememberMe);
return RedirectToAction("index", "home");
}
}
}
}
else
{
return RedirectToAction("index", "home");
}
return View();
}
}
And of course the autofac bootstrapper:
private static void SetAutoFacContainer()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerHttpRequest();
builder.RegisterAssemblyTypes(typeof(UserRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerHttpRequest();
builder.RegisterAssemblyTypes(typeof(ConnectionService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerHttpRequest();
builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>().InstancePerLifetimeScope();
builder.RegisterModule(new AutofacWebTypesModule());
builder.Register(att => new AuthorizeFilter(att.Resolve<IConnectionService>(), att.Resolve<IEmployeeLoginService>())).AsAuthorizationFilterFor<Controller>().InstancePerHttpRequest();
builder.RegisterFilterProvider();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
My idea how to do this, is setting a session variable with the connection string after data retrieval from the one static database where the info is stored and inject session in the unit of work and somehow use it there, but I can't wrap my head around it.
The question(s):
Am I heading in the right direction trying to achieve this, or even is it possible? If not what steps would you take to achieve this
I know it's a long read I hope you guys can help me, I'm quite new to using these techniques all together. Thanks in advance - I really appreciate it!
Your on the right track, I have used
var mtc = new MultitenantContainer(container.Resolve<ITenantIdentificationStrategy>(), container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));
The identification strategy would be based on the logged in user. With defaults for when they aren't logged in.
public class CompanyNameIdentificationStrategy : ITenantIdentificationStrategy
{
public bool TryIdentifyTenant(out object tenantId)
{
var context = HttpContext.Current;
if(context != null)
{
var myUser = context.User as MyUserObject;
if(myUser != null)
{
tenantId = myUser.CompanyName;
return true;
}
}
return false;
}
}
Then you add to your autofact setup:
var s = c.Resolve<ITenantIdentificationStrategy>();
object id;
if (s.TryIdentifyTenant(out id) && id != null)
{
return id;
}
return "default";
}).Keyed<string>("CompanyName");
builder.Register<Settings>(c =>
{
var companyName = c.ResolveKeyed<string>("companyName");
if (companyName == "default")
{
return new DefaultSettings();
}
var settings = new Settings();
return settings;
}).InstancePerLifetimeScope();
You can resolve stuff inside these code blocks. I would probably setup a keyed default settings, and then when the user is logged in the settings would switch to their setup and the rest of the application should work.
I am always getting NULL from a JOIN FETCH clause in my JPA Query, even though I have everything configured as expected:
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
#Entity
#Table(name = "TB_BANNER_IMAGE")
public class BannerImage extends BaseEntity<Integer> {
protected FileReference fileReference;
private String type;
private String labelTitle;
protected BannerImage() {}
#Id
#TableGenerator(name="genBannerImage", table="TB_ID_GENERATOR",
pkColumnName="ID_NAME", valueColumnName="ID_VAL",
pkColumnValue="TB_BANNER_IMAGE", allocationSize=1)
#GeneratedValue(strategy=GenerationType.TABLE, generator="genBannerImage")
#Column(name = "ID_BANNER_IMAGE", unique = true, nullable = false)
public Integer getId() {
return super.getId();
}
#Override
public void setId(Integer id) {
super.setId(id);
}
#Column(name="TYPE")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="ID_FILE_REFERENCE", nullable=false)
public FileReference getFileReference() {
return fileReference;
}
public void setFileReference(FileReference fileReference) {
this.fileReference = fileReference;
}
#Column(name="LABEL_TITLE")
public String getLabelTitle() {
return labelTitle;
}
public void setLabelTitle(String labelTitle) {
this.labelTitle = labelTitle;
}
}
for File Reference Class:
#Entity
#Table(name = "TB_FILE_REFERENCE")
public class FileReference extends BaseNamedEntity<String> {
private String type;
public FileReference() {}
#Id
#TableGenerator(name="genFileReference", table="TB_ID_GENERATOR",
pkColumnName="ID_NAME", valueColumnName="ID_VAL",
pkColumnValue="TB_FILE_REFERENCE", allocationSize=1)
#GeneratedValue(strategy=GenerationType.TABLE, generator="genFileReference")
#Column(name = "ID_FILE_REFERENCE", unique = true, nullable = false)
public String getId() {
return super.getId();
}
#Override
public void setId(String id) {
super.setId(id);
}
#Column(name = "TYPE")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Service class:
#Path("/banner")
public class BannerImageService extends BaseServiceFacade<BannerImage, Integer> {
#SuppressWarnings("unchecked")
#Override
public Crud<BannerImage, Integer> lookupService() throws ServiceLocatorException {
return ServiceLocator.getInstance()
.getLocalHome(ServicesConstants.BANNER_IMAGE_SERVICE);
}
#Override
protected String getDefaultGetQuery() {
return BannerImageDAO.GET_BY_ID_FETCH_FILE_REF;
}
#Override
protected String getDefaultQuery() {
return BannerImageDAO.GET_ALL_FETCH_FILE_REF;
}
}
get REST method of BaseServiceFacade:
#Override
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/{id}")
public T get(#PathParam("id") ID id) {
try {
if (!validateID(id)) {
logMessage("Invalid Entity ID: " + id);
return null;
}
String defaultGetQuery = getDefaultGetQuery();
if (defaultGetQuery != null) {
Map<String, Object> mapParams = new HashMap<String, Object>();
mapParams.put("id", id);
List<T> entityList = getService().search(defaultGetQuery, mapParams);
if (entityList != null && entityList.size() == 1) {
T ent = entityList.get(0);
return ent;
} else {
logMessage("Invalid search by Entity ID: " + id);
}
} else {
return getService().findById(clazz, id);
}
} catch (ServiceException e) {
serviceException(e);
} catch (Exception ex) {
logException(ex);
}
return null;
}
And finally the Service Bean EJB which reads from entityManager:
public class BaseServiceBean<T extends IEntity<ID>, ID extends Serializable> implements Crud<T,ID> {
// ... generic methods to be reused by subclasses
#Override
public List<T> search(String queryOrNamedQuery) throws ServiceException {
return search(queryOrNamedQuery, null, 0, 0);
}
#SuppressWarnings("unchecked")
public List<T> search(String namedQueryOrHql, Map<String, Object> parameters, int start, int chunkSize) {
try {
Query query = createQuery(namedQueryOrHql, getQueryType(namedQueryOrHql));
if (start > 0) {
query.setFirstResult(start);
}
if (chunkSize > 0) {
query.setMaxResults(chunkSize);
}
addParameters(query, parameters);
List<T> result = query.getResultList();
afterSearch(result);
return result;
} catch (NoResultException nre) {
nre.printStackTrace();
} catch (ClassCastException cce) {
cce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void afterSearch(List<T> result) {
}
// etc...
implementation specific class for BannerImageService:
#Stateless(mappedName="ejb/BannerImageService")
public class BannerImageServiceBean extends BaseServiceBean<BannerImage, Integer> implements BannerImageServiceBeanRemote, BannerImageServiceBeanLocal {
#Override
protected void afterSearch(List<BannerImage> result) {
if (result != null && result.size() == 1) {
BannerImage bannerImage = result.get(0);
bannerImage.getFileReference();
}
super.afterSearch(result);
}
// additional code ...
When I try to fetch my BannerImage class together with it's corresponding FileReference member I always get NULL even though in my DB there is an existing foreign key present:
JPQL:
"SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id";
Generated SQL:
SELECT t1.ID_BANNER_IMAGE, t1.LABEL_TEXT, t1.LABEL_TITLE, t1.TYPE,
t1.ID_FILE_REFERENCE, t0.ID_FILE_REFERENCE, t0.NAME,
t0.TYPE FROM TB_FILE_REFERENCE t0, TB_BANNER_IMAGE
t1 WHERE (t0.ID_FILE_REFERENCE = t1.ID_FILE_REFERENCE) AND t1.ID_BANNER_IMAGE = 1
in my DB the record shows a correct reference:
BANNER_IMAGE:
1;"";"main";"2bdbb063d0d0ee2939c89763945d9d9e";"banner1.png";"image/png"
If I execute :
select * from TB_FILE_REFERENCE where ID_FILE_REFERENCE = '2bdbb063d0d0ee2939c89763945d9d9e'
I can find the record in the DB, although my EclipseLink JPA Implementation always returns null:
EclipseLink Version 2.5.2-M1
This is how the Entity gets passed from Service Layer to the
Can someone help pointing why the JOIN FETCH is not properly working?
I faced a similar issue and looking closely I see that this issue was happening only to entities recently created/saved. Then I figured that it has something to do with eclipselink cache. I solved this problem by adding this line before making a join fetch JPQL query,
em.getEntityManagerFactory().getCache().evictAll();
em.createQuery("SELECT a FROM BannerImage a join fetch a.fileReference WHERE a.id = :id").getResultList();
HTH!
Have been implementing Generic Repository, Unit of Work pattern with EF5 Code First from a number of resources and have come up with the following assemblies.
Interfaces, Contexts, Model, Repositories, UnitsOfWork
In the Context assembly I have my migrations folder which contains Configuration.cs
internal sealed class Configuration : DbMigrationsConfiguration<Context.SportsContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(Context.SportsContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
As you can see this DbMigrationsConfiguration takes in my SportsContext which is also defined in the contexts assembly (Contexts folder)
public class SportsContext : IDbContext
{
private readonly DbContext _context;
public SportsContext()
{
_context = new DbContext("SportsContext");
}
public void Dispose()
{
_context.Dispose();
}
public IDbSet<T> GetEntitySet<T>() where T : class
{
return _context.Set<T>();
}
public void ChangeState<T>(T entity, EntityState state) where T : class
{
_context.Entry(entity).State = state;
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
This implements IDbContext which is defined in the Interfaces assembly
public interface IDbContext : IDisposable
{
IDbSet<T> GetEntitySet<T>() where T : class;
void ChangeState<T>(T entity, EntityState state) where T : class;
void SaveChanges();
}
In my UnitsOfWork assembly I have the following class
public class SportUnitOfWork : IUnitofWork
{
private readonly IDbContext _context;
public SportUnitOfWork()
{
_context = new SportsContext();
}
private GenericRepository<Team> _teamRepository;
private GenericRepository<Fixture> _fixtureRepository;
public GenericRepository<Team> TeamRepository
{
get { return _teamRepository ?? (_teamRepository = new GenericRepository<Team>(_context)); }
}
public GenericRepository<Fixture> FixtureRepository
{
get { return _fixtureRepository ?? (_fixtureRepository = new GenericRepository<Fixture>(_context)); }
}
public void Save()
{
_context.SaveChanges();
}
public IDbContext Context
{
get { return _context; }
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
For examples sake I have added the GenericRepository class in the Repositories assembly
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private IDbContext _context;
public GenericRepository(IDbContext context)
{
_context = context;
}
public GenericRepository(IUnitofWork uow)
{
_context = uow.Context;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_context == null) return;
_context.Dispose();
_context = null;
}
public void Add(T entity)
{
_context.GetEntitySet<T>().Add(entity);
}
public void Update(T entity)
{
_context.ChangeState(entity, EntityState.Modified);
}
public void Remove(T entity)
{
_context.ChangeState(entity, EntityState.Deleted);
}
public T FindSingle(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
{
var set = FindIncluding(includes);
return (predicate == null) ? set.FirstOrDefault() : set.FirstOrDefault(predicate);
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
{
var set = FindIncluding(includes);
return (predicate == null) ? set : set.Where(predicate);
}
public IQueryable<T> FindIncluding(params Expression<Func<T, object>>[] includeProperties)
{
var set = _context.GetEntitySet<T>();
if (includeProperties != null)
{
foreach (var include in includeProperties)
{
set.Include(include);
}
}
return set.AsQueryable();
}
public int Count(Expression<Func<T, bool>> predicate = null)
{
var set = _context.GetEntitySet<T>();
return (predicate == null) ? set.Count() : set.Count(predicate);
}
public bool Exist(Expression<Func<T, bool>> predicate = null)
{
var set = _context.GetEntitySet<T>();
return (predicate == null) ? set.Any() : set.Any(predicate);
}
}
The problem I have is in the Configuration class which inherits from DbMigrationsConfiguration is expecting a DbContext parameter.
Error is Error 1 The type 'Contexts.Context.SportsContext' cannot be used as type parameter 'TContext' in the generic type or method 'System.Data.Entity.Migrations.DbMigrationsConfiguration'. There is no implicit reference conversion from 'Contexts.Context.SportsContext' to 'System.Data.Entity.DbContext'.
I can change the SportsContext to also inherit from DbContext but then I need to add a reference to EntityFramework 5 in the UnitsOfWork assembly as we want to possibly change or take out each layer without any reference to underlying models which is why i went with this pattern.
As we are looking at adding further contexts and models in the future so wanted to setup a architecture in that we could just add the context, model and then implement the relevant interfaces as and when needed.
A WebAPI Restful Web Service will be interacting with our data via the SportUnitOfWork, if I have understood the patterns correctly.
If anyone has any ideas on how I could do this or anything that I am doing wrong please let me know
thanks in advance Mark
Resolved this by doing the following
Changed my SportsContext class to a BaseContext which is abstract
public abstract class BaseContext : IDbContext
{
protected DbContext Context;
public void Dispose()
{
Context.Dispose();
}
public IDbSet<T> GetEntitySet<T>() where T : class
{
return Context.Set<T>();
}
public void Add<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Added;
}
public void Update<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Modified;
}
public void Delete<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Deleted;
}
public void SaveChanges()
{
// At the moment we are conforming to server wins when handling concurrency issues
// http://msdn.microsoft.com/en-us/data/jj592904
try
{
Context.SaveChanges();
}
catch (DbUpdateConcurrencyException e)
{
//Refresh using ServerWins
var objcontext = ((IObjectContextAdapter) Context).ObjectContext;
var entry = e.Entries;
objcontext.Refresh(RefreshMode.StoreWins, entry);
SaveChanges();
}
}
private DbEntityEntry GetDbEntityEntrySafely<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = Context.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
// Set Entity Key
var objcontext = ((IObjectContextAdapter) Context).ObjectContext;
if (objcontext.TryGetObjectByKey(dbEntityEntry.Entity))
Context.Set<T>().Attach(entity);
}
return dbEntityEntry;
}
}
created in the Context folder a new class called FootballContext which inherits from BaseContext.
public class FootballContext : BaseContext
{
public FootballContext(string connectionstringName)
{
Context = new BaseFootballContext(connectionstringName);
}
}
Created a new folder called DbContexts
In here created the following classes,
public class BaseFootballContext : DbContext
{
public BaseFootballContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
public IDbSet<Fixture> Fixtures { get; set; }
public IDbSet<Team> Teams { get; set; }
}
public class MigrationsContextFactory : IDbContextFactory<BaseFootballContext>
{
public BaseFootballContext Create()
{
return new BaseFootballContext("FootballContext");
}
}
now my Configuration class can take in the BaseFootballContext as this is a DbContext.
My UnitOfWork class can now set the context to be FootballContext so does not have to reference EntityFramework.
This works with Migrations as well.
Only problem I have now is to figure out how to get this to work in a disconnected environment as I am having a problem reattaching entities and applying updates.
I'm looking for good practices and clean solution to my problem.
There are 2 entity classes:
#Entity
public class Site implements Serializable {
...
#OneToMany(mappedBy = "site")
private List<SiteIp> siteIpList;
...
public List<SiteIp> getSiteIpList() {
return siteIpList;
}
public void setSiteIpList(List<SiteIp> siteIpList) {
this.siteIpList = siteIpList;
}
}
#Entity
#IdClass(SiteIpPK.class)
public class SiteIp implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Site site;
#Id
private int idx;
private String ip;
/* other stuff such as constructors, getters and setters */
}
SiteIpPK has 2 columns, it should be clean :)
Obviously, this is commonly used model in world.
Next, there is a view layer. JSF page has 3 fields showing SiteIp.ip for given site and idx. As far, I've wrote helper getter method on Site entity:
public String getIpForIdx(Integer idx) {
// there's no simple siteIpList.get(idx), because of additional logic in getter
// so let's iterate through entire list
for (SiteIp siteIp : this.siteIpList) {
if (/* other logic returns true value && */ siteIp.getIdx() == idx) {
return siteIp.getIp();
}
}
return null;
}
JSF expression language is constructed as follows:
<h:inputText id="sync1_ip" value="#{siteController.editContext.site.getIpForIdx(1)}" />
Now, when page is accessed, proper IP values is propagated to input text field, as far it's all good. But, when IP changed and form is submitted, EL throws exception:
javax.el.PropertyNotWritableException: /edit/siteEdit.xhtml #47,123 value="#{siteController.editContext.site.getIpForIdx(1)}": Illegal Syntax for Set Operation
I understand and acknowledge that, so there is my question:
What are best practices to handle this issue? Somewhat awful solution is to write as many helper methods as unique indexes exists:
public String getIp1() {
return this.IpForIdx(1);
}
public String getIp2() {
return this.IpForIdx(2);
}
/* ... */
public void setIp1(String newIp) {
this.siteIpList.add(1, newIp);
}
public void setIp2(String newIp) {
this.siteIpList.add(2, newIp);
}
/* ... and so on... */
with JSF EL:
<h:inputText id="sync1_ip" value="#{siteController.editContext.site.ip1}" />
Are there other, more flexible and beautiful solutions?
You should better google for 'composite primary key with JPA'. But as you're new here, below goes a simple explanation and a working solution.
Small explanation
Composite primary keys are themselves classes with #Embeddable annotation. And they become a primary key in your #Entity class as soon as you mark your primary composite key field with #EmbeddedId. The only thing left is to implement equals and hashcode appropriately in both classes.
Solution
The solution consists of an #Entity class
#Entity
#Table(name="...")
#AssociationOverride(name = "pk.site", joinColumns = #JoinColumn(name = "..."))
public class SiteIp implements Serializable {
private SiteIpPk pk = new SiteIpPK();
private String ip;
public SiteIp() { }
#EmbeddedId
public SiteIpPK getPk() {
return pk;
}
public void setPk(SiteIpPk pk) {
this.pk = pk;
}
#Transient
public Site getSite() {
return pk.getSite();
}
public void setSite(Site site) {
pk.setSite(site);
}
#Transient
public Integer getIdx() {
return pk.getIdx();
}
public void setIdx(Integer idx) {
pk.setIdx(idx);
}
#Column(name="...")
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SiteIp that = (SiteIp) o;
if (pk != null ? !pk.equals(that.pk) : that.pk != null)
return false;
return true;
}
public int hashCode() {
return (pk != null ? pk.hashCode() : 0);
}
}
and #Embeddable class
#Embeddable
public class SiteIpPk implements Serializable {
private Site site;
private Integer idx;
#ManyToOne
public Site getSite() {
return site();
}
public void setSite(Site site) {
this.site = site;
}
#Column(name="...", nullable = false)
public Integer getIdx() {
return idx;
}
public void setIdx(Integer idx) {
this.idx = idx;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SiteIpPk that = (SiteIpPk) o;
if (site != null ? !site.equals(that.site) : that.site != null)
return false;
if (idx != null ? !idx.equals(that.idx) : that.idx != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (site != null ? site.hashCode() : 0);
result = 17 * result + (idx != null ? idx.hashCode() : 0);
return result;
}
}
In the end, learn more yourself and check if the problem has already been investigated!