I'm try to do this : I'm using EF code first to map an old existing database. There's many fields with the wrong type (ex: char(1) used as boolean) so I've created a wrapper class for my db context that maps perfectly to the database table. Now, I want to expose an IQueryable of my Entity type on my repository. See my example :
public class MyContext:DbContext
{
public DbSet<WrappedEntity> WrapperEntities;
}
public class Repository
{
private MyContext _context;
//contructors omitted
public IQueryable<Entity> GetEntities()
{
return _context.WrapperEntities; //doesn't compile, I need some convertion here
}
}
I already have my convertion routine, the only thing missing is a way to query my DbContext thought my repository without exposing the WrappedEntity class, Is it possible and how ?
Thanks.
Usually you project with Queryable.Select to change the type of your query...
public IQueryable<Entity> GetEntities()
{
return _context.WrapperEntities.Select(x => new Entity(){...});
}
Related
I am new to the MVVM architecture in Android, and I have some days with a doubt that I consider basic, but that I can't solve.
I proceed to discuss my problem:
I have an Entity, CustomerView (this entity is created from a DatabaseView):
#DatabaseView("select ... ")
public class CustomerView {
public String cardCode;
public String cardName;
public String cardFName;
...
Then, I have a Dao class:
#Dao
public interface OCRD_DAO {
...
#Query("SELECT * from CustomerView where cardCode= :cardCode")
LiveData<CustomerView> getCustomerViewByCardCode(String cardCode);
...
}
The repository class, makes use of the DAO class:
public LiveData<CustomerView> getCustomer(String cardCode){
return mOcrdDao.getCustomerViewByCardCode(cardCode);
}
The CustomerSheetViewModel class:
public class CustomerSheetViewModel extends BaseObservable {
private Repository mRepository;
public LiveData<CustomerView> mCustomer;
private MutableLiveData<String> _cardName;
#Bindable
public MutableLiveData<String> getCardName(){
return this._cardName;
}
public MutableLiveData<String> setCardName(String value){
// Avoids infinite loops.
if (mCustomer.getValue().cardName != value) {
mCustomer.getValue().cardName = value;
// React to the change.
saveData();
// Notify observers of a new value.
notifyPropertyChanged(BR._cardName);
}
}
public CustomerSheetViewModel (Application application, String cardCode) {
mRepository = new Repository(application);
this.mCustomer = mRepository.getCustomer(cardCode);
//Init MutableLiveData????
this._cardName = this.mCustomer.getValue().cardName;
//Null Exception, because this.mCustomer.getValue() is null
}
}
At this point, my problem occurs: when I initialise the CustomerView object, it is of type LiveData. However, if I want to make use of 2-way binding, I need an object of type MutableLiveData. So, I think I should create the MutableLiveData object with the data extracted from the database (i.e. from the call to the repository). When I try this (e.g. getValue().cardName) a null exception is thrown, since LiveData is asynchronous.
Finally, I could make use of this property in the layout:
android:text="#={customerSheetViewModel.cardName}"
I really appreciate any help, as I can't find any reference to 2-way binding when the data comes from a database read.
Thanks in advance.
I would like a custom entity listener to generate an auto-incremented alias for a few of the entities.
I have implemented one util class in order to generate auto incremented alias for the entities in a distributed environment as follows:
#Component
public class AutoIncrementingIdGenerationUtil {
private final RedisTemplate<String, Object> redisTemplate;
public AutoIncrementingIdGenerationUtil(
RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public String getNextSequenceNumber(String keyName) {
RedisAtomicLong counter = new RedisAtomicLong(keyName,
Objects.requireNonNull(redisTemplate.getConnectionFactory()));
return counter.incrementAndGet();
}
}
Now, I have several entities in my application, for a FEW OF ENTITIES, I would like to generate the alias.
So I am writing my own custom entity listener as follows:
#Component
public class CustomEntityListener<T> {
private final AutoIncrementingIdGenerationUtil autoIncrementingIdGenerationUtil;
public CustomEntityListener(
AutoIncrementingIdGenerationUtil autoIncrementingIdGenerationUtil) {
this.autoIncrementingIdGenerationUtil = autoIncrementingIdGenerationUtil;
}
#PrePersist
void onPrePersist(Object entity) { <----HERE I WOULD LIKE TO CAST TO CONCRETE ENTITY TYPE,
if(StringUtils.isBlank(entity.getAlias())) {
entity.setAlias(autoIncrementingIdgenerationUtil.getNextSequenceNumber(entity.getEntityType());
}
}
As mentioned above, all of the entities do not have an alias attribute. I am not getting any proper idea regarding how to do this. One bad idea is to use getTEntityype(). But in this case, it would be too many if-else and typecast accordingly, which will not look good. Any better idea regarding how to do it?
Another related question in the same context, if I have an entity having a #PrePersist function already, will the function defined in entity listener override this, OR will both of them run?
Entity listeners cannot be parameterized. Just make the relevant entities implement an interface, e.g. Aliased, with a setAlias() method. You'll then have a single type to cast to.
Also, why use Redis? Doesn't your DB have sequences?
I'm new to MVC and the EF. My app is a simple code-first with several POCO classes and a DBContext like this:
public class ExpDefContext : DbContext
{
public DbSet<Experiment> Experiments { get; set; }
public DbSet<Research> Researches { get; set; }
...
The problem: I need to add to my data model an entity-set that its type is built at runtime from user input, meaning I have no idea of its data structure.
I read the non-generic Dbset class is made just for this, so I added to the context:
public DbSet Log { get; set; }
...and created a constructor for the context that accepts the runtime-type and sets the new Dbset:
public ExpDefContext(Type LogRecType)
{
Log = Set(LogRecType);
}
(the type by the way is built using Reflection.Emit).
In the controller I create the type (named LogRec) and pass it to a new DBContext instance. Then I create a LogRec instance and try to Add it to the database:
Type LogRec;
LogRec = LogTypeBuilder.Build(dbExpDef, _experimentID);
var dbLog = new ExpDefContext(LogRec);
var testRec = LogRec.GetConstructor(Type.EmptyTypes).Invoke(Type.EmptyTypes);
dbLog.Log.Add(testRec);
dbLog.SaveChanges();
and I get an exception from the dbLog.Log.Add(testRec):
The entity type LogRec is not part of the model for the current context
What am I doing wrong?
Is there a better way to do this (preferably without diving too deep into the Entity Framework)?
Thanks
I suspect that EF only reflects over the generic DbSet<T> properties in your derived DbContext and ignores any non-generic DbSet properties when the model is created in memory.
However, an alternative approach might be to use the Fluent API in OnModelCreating to add your dynamic type as an entity to the model.
First of all you can add a type to the model only when the model is built in memory for the first time your AppDomain is loaded. (A model is built only once per AppDomain.) If you had a default constructor of the context in addition to the overloaded constructor and had created and used a context instance using this default constructor your model would have been built with only the static types and you can't use the dynamic type as entity anymore as long as the AppDomain lives. It would result in exactly the exception you have.
Another point to consider is the creation of the database schema. If your type is unknown at compile time the database schema is unknown at compile time. If the model changes due to a new type on the next run of your application you will need to update the database schema somehow, either by recreating the database from scratch or by defining a custom database initializer that only deletes the LogRec table and creates a new table according to the new layout of the LogRec type. Or maybe Code-First Migrations might help.
About the possible solution with Fluent API:
Remove the DbSet and add a Type member instead to the context and override OnModelCreating:
public class ExpDefContext : DbContext
{
private readonly Type _logRecType;
public ExpDefContext(Type LogRecType)
{
_logRecType = LogRecType;
}
public DbSet<Experiment> Experiments { get; set; }
public DbSet<Research> Researches { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
entityMethod.MakeGenericMethod(_logRecType)
.Invoke(modelBuilder, new object[] { });
}
}
DbModelBuilder doesn't have a non-generic Entity method, hence dynamic invocation of the generic Entity<T> method is necessary.
The above code in OnModelCreating is the dynamic counterpart of...
modelBuilder.Entity<LogRec>();
...which would be used with a static LogRec type and that just makes the type as entity known to EF. It is exactly the same as adding a DbSet<LogRec> property to the context class.
You should be able to access the entity set of the dynamic entity by using...
context.Set(LogRecType)
...which will return a non-generic DbSet.
I have no clue if that will work and didn't test it but the idea is from Rowan Miller, member of the EF team, so I have some hope it will.
I am using Entity Framework 4.1 code first in an MVC 3 app.
I have the following repository:
public class BankRepository : IBankRepository
{
HefContext db = new HefContext();
public ICollection<Bank> GetAll()
{
return db.Banks;
}
}
I get an error when returning db.Banks. I'm not sure what it means, can someone please help clarify and how to change it so that the error goes away? The error is:
Cannot implicitly convert type 'System.Data.Entity.DbSet<MyProject.Core.DomainObjects.Bank>' to 'System.Collections.Generic.ICollection<MyProject.Core.DomainObjects.Bank>'. An explicit conversion exists (are you missing a cast?)
What is returned by db.Banks? An IEnumerable?
db.Banks is of type DbSet. This class does not implement ICollection interface. Change the return type of the method to IQueryable<Bank> or IEnumerable<Bank>.
public class BankRepository : IBankRepository
{
HefContext db = new HefContext();
public IQueryable<Bank> GetAll()
{
return db.Banks;
}
}
ICollection is used only as the backing property to support LazyLoading, not as the result of a method. Check here ;)
I have a CRUD repository as fallowing:
public class CrudRepository<T> : ICrudRepository<T>
where T : class, IUnique
{
static DataContext db = new DataContext();
protected DataContext DataContext { get { return db; } }
public virtual IQueryable<T> GetAll()
{
return db.GetTable<T>();
}
public virtual void Add(T item)
{
db.GetTable<T>().InsertOnSubmit(item);
}
public virtual void Save()
{
db.SubmitChanges();
}
public virtual T Get(int id)
{
return GetAll().FirstOrDefault(t => t.Id.Equals(id));
}
}
I use static data context for all instance off repository.
I want to change foreign key entity so i try fallowing solution:
CrudRepository<Employee> employeeRepository = new CrudRepository<Employee >();
Employee employee = employeeRepository.Get(employeeId)
employee.OfficeId = officeId;
employeeRepository.Save();
But it throw fallowing exception :
ForeignKeyReferenceAlreadyHasValueException
So i try fallowing second solution:
CrudRepository<Employee> employeeRepository = new CrudRepository<Employee >();
Employee employee = employeeRepository.Get(employeeId)
employee.Office = new CrudRepository<Office>().Get(officeId);
employeeRepository.Save();
But it throw exception with fallowing message:
An attempt has been made to Attach or
Add an entity that is not new, perhaps
having been loaded from another
DataContext
what can i do?
what is the problem?
Three things jump out at me.
employee.OfficeId = officeId;
If the Employee class has an OfficeId property and an Office property, you must use the Office property to make changes. The Office property is auto-generated from the relationship in the linq designer.
If you want to use Id based manipulations instead, delete the relationship between employee and office in the designer (note: this does not change the database, it just changes the mappings used by the code generator).
new CrudRepository<Employee >();
new CrudRepository<Office>().Get(officeId);
Each Crud Repository has its own datacontext. Objects loaded from different datacontexts are not allowed to co-mingle. Suppose they were allowed to co-mingle - when you call SubmitChanges, which DataContext is responsible for saving?
Ultimately, this means your CrudRepository implementation is going to be something you want to move away from if you want to continue using LinqToSql. Supporting Crud operations on a single class just isn't that useful. At least it's only passthrough calls, and will be easy to replace with direct DataContext method calls.
static DataContext db = new DataContext();
This is damnable. DataContext is not threadsafe.