DbContext Generic Method to Find Key Members - ef-code-first

Is there a way to find Key Members of an entity (TEntity) when using CodeFirst (DbContext) on a generic context similar to ObjectContext?
I can see how it's done using ObjectContext here.

I'm sorry for the confusion, it seems like even with Code First approach we can find the key members on an entity using the object context as shown below:
var ObjectContext = ((IObjectContextAdapter)DbContext).ObjectContext;
var ObjectSet = ObjectContext .CreateObjectSet<TEntity>();
var EntitySet = ObjectSet.EntitySet;
var KeyMembers = EntitySet.ElementType.KeyMembers;

Related

Linq To Update One Complete Row

So I would like to update/replace one complete row with the data I have.
Instead of doing
entityA.Name = entityB.Name
I would like to say something like entityA = entityB, where entitiyB is same as entityA but has new values.
Please let me know.
Thanks..
simply doing entity1 = entity2 would change the entity1 reference and hence the context wont track the new reference ay more. Thus the changd value wont be updated.
you should be using an object mapper for that. i personally use GLUE that can be installed using command Install-Package Glue on the package manager console
There are others too like AutoMapper
if you go with glue then just make a mapping like
var mapping = new Mapping<entity1, entity2>();
mapping.AutoRelateEqualNames();
//or define a mapping using .Relate()
//then call map
mapping.Map(entity1)//this will return entity2
or if you already have an object tracked by a context class call
entity2 = mapping.Map(entity1, entity2);

EF 5.0 Trouble updating entity which is already tracked

I'll preface this question with the following: I know there are a million posts on the internet about the old "An object with the same key already exists in the ObjectStateManager" issue. My scenario is a bit more complicated, I think.
I have a UnitOfWork class which creates a DbContext and passes it to any repository which is called. The pattern I'm using closely follows the Unit of Work tutorial on the ASP.NET site. Unlike the tutorial, my repositories take in Business entities, map them to data entities, and perform some CRUD action. My Business logic only works with Business entities. Here is what I'm trying to do in a sample Business Manager class:
_unitOfWork.Repository.Add(entity);
_unitOfWork.Save(); // context.SaveChanges() under the hood
...Perform some operations on the model...
_unitOfWork.Repository.Update(entity);
_unitOfWork.Save();
Here is a sample Update method from the repository:
public virtual void Update(entity)
{
var dataEntity = // map from business entity to data;
_context.Entry(dataEntity).State = EntityState.Modified;
}
It obviously fails on the last line. Here is where my confusion sets in:
The entity's State is Detached
When I attempt to change the State to Modified or Unchanged, it gives me the ObjectStateManager exception above.
When I attempt to detach the entity from the context (((IObjectContextAdapter)_context).ObjectContext.Detach(entity);) I get an exception about how the entity is not attached to the context, therefore, it cannot detach it. Very confusing (something fundamental I'm missing, for sure).
Many other posts suggest I make a database call, update that entity in the repository, then _unitOfWork.Save(). I don't like this approach. I shouldn't need to make an unnecessary network call to update an entity.
The Update method in the repository needs to handle two scenarios: 1) updating an entity which is not currently tracked by the context, and 2) updating an entity which IS currently tracked by the context. The second piece is what I'm struggling with.
Any help or insight is appreciated.
Thanks!
This means that there already is an object attached to the context with the same key as the new dataEntity. The existing object and the new entity both represent the same entry in the database but they are two different objects.
This may indicate that the lifespan of your _context is too long, but that's hard to judge from your code. It is certain though that the context was previously used to fetch an entity from the database that is subsequently duplicated by var dataEntity = ....
You may have to shorten the lifespan of the context, I can't tell. If you think it's OK you may want to use the Local collection to check whether the entity is already there. That will save the database round trip that Find may still make.
I found a hybrid solution which appears to work:
public virtual void Update(TB entity)
{
var dataEntity = Mapper.Map<TB, TD>(entity);
var pkey = _dbSet.Create().GetType().GetProperty("Id").GetValue(dataEntity);
var entry = _context.Entry(dataEntity);
if (entry.State == EntityState.Detached)
{
var attachedEntity = _dbSet.Find(pkey);
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(dataEntity);
}
else
{
entry.State = EntityState.Modified;
}
}
else
{
entry.State = EntityState.Modified;
}
}

Breeze: How can I create a GUID key for new entities on the client?

Using Breeze, what is the simplest way to populate a GUID key when an entity is created?
I'll assume that your entity is configured such that the client is responsible for setting the Guid key for new entities. That's the default for the Guid key of an Entity Framework Code First entity; it is as if the key property were adorned with [DatabaseGenerated(DatabaseGeneratedOption.None)]
The obvious approach is to set the key after creating the entity and before adding it to the manager, e.g.:
function createFoo() {
var foo = fooType.createEntity();
foo.id(breeze.core.getUuid()); // Knockout implementation
manager.addEntity(foo);
}
This may be all you ever need.
On the other hand, you may find that you're creating new Foos in many places and for some strange reason you can't use the createFoo function. You certainly don't want to repeat that code.
You can extend the Foo entity type with id-setting behavior after which you'd be able to write:
function createFoo() {
var foo = fooType.createEntity(); // foo.id is set for you
manager.addEntity(foo);
}
There are two approaches to consider - custom constructor and type initializer; both are described in "Extending Entities"
Constructor
You can initialize the key inside a custom constructor. Breeze calls the constructor both when you create the entity and when it materializes a queried entity. Breeze will replace the initial key value when materializing.
Here's an example that assumes the Knockout model library.
function Foo() {
foo.id(breeze.core.getUuid()); // using KO
}
// one way to get the MetadataStore
var store = manager.metadataStore;
// register the ctor with the Foo type
store.registerEntityTypeCtor("Foo", Foo);
Pretty simple. The only downside is that Breeze will generate a Guid every time it makes an entity, whether creating a new one or materializing one from a query. It's wasted effort during materialization but so what? Well, I suppose that might become a performance issue although I wouldn't assume so until I had measured it.
Initializer
Suppose you measured and the repeated Guid generation is a serious problem (really?). You could set the key in a type initializer instead and only call the Guid generator when creating a new entity.
Breeze calls a type initializer after the entity has been created or materialized from query just before returning that entity to the application. Clearly you don't want to overwrite a materialized key from the database so you'll test the key value to make sure it's not real (i.e. to make sure you're fixing a created entity) before assigning it. Here's an example.
function fooInitializer(foo) {
var emptyGuid = "00000000-0000-0000-0000-000000000000";
if (foo.id() !=== emptyGuid) {
foo.id(breeze.core.getUuid());
}
}
var store = manager.metadataStore;
// register the initializer; no ctor in this example
store.registerEntityTypeCtor("Foo", function(){}, fooInitializer);
Assuming you have a Guid surrogate Key on all your entities like we have in our case, you could code a createInstance factory that does the following in a very generic approach:
function createInstance(breezeEntityManager, typeName) {
var keyProperty = breezeEntityManager.metadataStore.getEntityType(typeName, false).dataProperties.filter(function (p) {
return p.isPartOfKey;
})[0];
var config = {};
config[keyProperty.name] = breeze.core.getUuid();
return breezeEntityManager.createEntity(typeName, config);
}
This way, you won't have to create an initializer for all your entities.

ASP.Net Entity Framework Repository & Linq

My scenario:
This is an ASP.NET 4.0 web app programmed via C#
I implement a repository pattern. My repositorys all share the same ObjectContext, which is stored in httpContext.Items. Each repository creates a new ObjectSet of type E. Heres some code from my repository:
public class Repository<E> : IRepository<E>, IDisposable
where E : class
{
private DataModelContainer _context = ContextHelper<DataModelContainer>.GetCurrentContext();
private IObjectSet<E> _objectSet;
private IObjectSet<E> objectSet
{
get
{
if (_objectSet == null)
{
_objectSet = this._context.CreateObjectSet<E>();
}
return _objectSet;
}
}
public IQueryable<E> GetQuery()
{
return objectSet;
}
Lets say I have 2 repositorys, 1 for states and 1 for countrys and want to create a linq query against both. Note that I use POCO classes with the entity framework. State and Country are 2 of these POCO classes.
Repository stateRepo = new Repository<State>();
Repository countryRepo = new Repository<Country>();
IEnumerable<State> states = (from s in _stateRepo.GetQuery()
join c in _countryRepo.GetQuery() on s.countryID equals c.countryID
select s).ToList();
Debug.WriteLine(states.First().Country.country)
essentially, I want to retrieve the state and the related country entity. The query only returns the state data... and I get a null argument exception on the Debug.WriteLine
LazyLoading is disabled in my .edmx... thats the way I want it.
You're doing a join without retrieving anything from it. There are multiple solutions to your problem:
Use Include to load the dependent entities: from s in ((ObjectSet<State>) _stateRepo.GetQuery).Include("Country"). The problem with this approach is that you should expose the ObjectSet directly rather than as a IQueryable if you want to avoid casting.
Use context.LoadProperty(states.First(), s => s.Country) to explicitly load the Country from the database for a given state.
Select both entities in the query: from s in ... join c ... select new { s, c }. You won't be able to access directly the state's Country property but you have it in the anonymous type.
Enable lazy loading.
Your repository implementation is very similar to mine, especially the way you are storing the ObjectContext. It works fine for me, so I don't think it's a conceptual problem.
Try using a static objectcontext (no wrapper) just to see if that fixes the problem. Perhaps there is a bug in your ContextHelper which causes your context to get disposed and recreated.

C# CF2.0 - System.Activator and Internal classes

I've got a data provider that contains a collection of entities. I only want to be able to create a new entity through the data provider.
I.e, to create a new record I need to use:
Entity entity = Provider.AddNew();
enity.set_Properties... etc
My issue is that if I set my entities to Internal, System.Activator cannot create an Instance of them. Each of my Data Providers uses a Base class with the generic type of the entity passed through.
So at the moment my AddNew() method contains the following:
public T AddNew()
{
T added = Activator.CreateInstance<T>();
this.Collection.Add(added);
return added;
}
It's obviously not the end of the world if I can instantiate a new entity manually outside of the Data Provider namespaces, but it seems pointless considering there's no way to ever save them, so why give the option to do so?
EDIT: Forgot to mention that all my providers, entities, etc are in the same namespace.
Don't use the Activator, which relies on a public constructor. Instead use reflection to find the parameterless constructor and then call it. Something along these lines:
Type t = typeof(MyType);
var parameterlessCtor = (from c in t.GetConstructors(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
where c.GetParameters().Length == 0
select c).FirstOrDefault;
if(parameterlessCtor != null) instance = parameterlessCtor.Invoke(null);

Resources