November 2008 - Posts

How to have all the no deleted entities?

With Northwind, if you do this:

context.Categories

you will get all the categories which are in DB. It means that you won’t have context added categories and you will have deleted entities.

Now, if you do this:

context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged).Select(ose => ose.Entity).OfType<Category>()

you will have only the entities already loaded into the context.

To have all the entities without deleted one, you can do this:

context.Categories.AsEnumerable().Where(c => c.EntityState != EntityState.Deleted).Union(context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(ose => ose.Entity).OfType<Category>())

Posted by Matthieu MEZIL | with no comments

How to delete all the related entities?

With Northwind, how to delete all the orders of a customer?

You can do this:

foreach (var o in customer.Orders.ToArray())

    context.DeleteObject(o);

but in this case, you will only delete the orders loaded in the context.

To delete all the DB customer orders, you can do this:

foreach (var o in customer.Orders.CreateSourceQuery())

    context.DeleteObject(o);

This code will load all the customer orders and will delete each of them.

Now, how to delete all the related entities with no information about the entity?

You can do this:

public static void DeleteRelatedEntities(this EntityObject entity, ObjectContext context)

{

    foreach (var relatedEntity in (((IEntityWithRelationships)entity).RelationshipManager.GetAllRelatedEnds().SelectMany(re => re.CreateSourceQuery().OfType<EntityObject>()).Distinct()).ToArray())

            context.DeleteObject(relatedEntity);

}

Posted by Matthieu MEZIL | with no comments

How to split a data table v2?

I wrote a post which explains how to split a data table.

But there is a bug in EF and so I couldn't add a new Category with this EDM. Colin wrote about this bug here.

So, in my last EDM, I just have to reverse the Principal and the Dependant roles of my association:

<Association Name="CategoryCategoryPicture">

    <End Type="NorthwindModel.Category" Role="Category" Multiplicity="1" />

    <End Type="NorthwindModel.CategoryPicture" Role="CategoryPicture" Multiplicity="1" />

    <ReferentialConstraint>

        <Principal Role="CategoryPicture">

            <PropertyRef Name="CategoryID" />

        </Principal>

        <Dependent Role="Category">

            <PropertyRef Name="CategoryID" />

        </Dependent>

    </ReferentialConstraint>

</Association>

Posted by Matthieu MEZIL | with no comments

EF: Attach a graph

In a lot of cases (WCF scenario for example) we need to attach a graph to a context. But the graph can be modified when it is detached. So you have to apply modifications when you attach it back. To do this, ObjectContext class has a method ApplyPropertyChanges. This method only applies to the scalar properties, so it’s insufficient for graphs.

Moreover, there is a problem with attachment. When you want to attach a graph, if some entities are new (EntityKey equals null), you will have an exception. So you must break the graph and add or attach the entity in accordance to whether they are new or not.

In a graph, you can:

  • Modify an entity
  • Add an entity and link it to the graph
  • Add a relation between two entities of the graph
  • Delete a relation between two entities of the graph
  • Delete an entity from the graph

If you work outside of the context, I think it’s a pity to report the removal of an entity to a context. Why?

In fact, in a WCF scenario for example, you never get the same ObjectContext. So to delete an entity, it means that you need to load it from DB to delete it then. In this case, I prefer to use a SSDL Function (which can be a DB stored procedure) to delete it directly with just the entity key.

For many to many relationship, there is a lot of chance that if you delete the association between the two entities, you will lose one entity on the graph. As my AttachGraph method doesn’t take the original graph as parameter, the method can’t determine if the graph loses this entity (in comparison to the DB) because the association was deleted or because the entity wasn’t in the original graph. So my method doesn’t support this scenario. For zero or one to many relationships, my method reports it only if the entity which has the EntityReference property stayed in the graph. It supports all other modification cases.

public static class AttachExtension

{

    public static void AttachGraph(this EntityObject entity, ObjectContext context, Func<ObjectContext> createContext)

    {

        if (entity == null)

            return;

        bool b;

        entity.AttachGraph(context, createContext, out b);

    }

    public static void AttachGraph(this EntityObject entity, ObjectContext context, Func<ObjectContext> createContext, out bool isNewEntity)

    {

        IEnumerable<string> changeProperties;

        isNewEntity = entity.IsNewEntity(createContext, out changeProperties);

        if (entity.EntityState != EntityState.Detached)

            (entity as IEntityWithChangeTracker).SetChangeTracker(null);

        AttachOrAddGraph(context, entity, isNewEntity, changeProperties, createContext);

    }

 

    private static bool IsNewEntity(this EntityObject entity, Func<object, bool> entityMap)

    {

        return entityMap(entity);

    }

    private static bool IsNewEntity(this EntityObject entity, Func<ObjectContext> createContext, out IEnumerable<string> changedProperties)

    {

        IEnumerable<string> changedPropertiesValue = new string[0];

        var value = IsNewEntity(entity, e =>

        {

            var entityWithKey = (IEntityWithKey)e;

            if (entityWithKey.EntityKey == null || entityWithKey.EntityKey.EntityKeyValues == null)

                return true;

            using (var context2 = createContext())

            {

                object outEntity;

                if (context2.TryGetObjectByKey(entityWithKey.EntityKey, out outEntity))

                {

                    var entity2 = outEntity as EntityObject;

                    context2.ApplyPropertyChanges(entity2.EntityKey.EntitySetName, entity);

                    changedPropertiesValue = context2.ObjectStateManager.GetObjectStateEntry(entity2).GetModifiedProperties();

                    return false;

                }

                return true;

            }

        });

        changedProperties = changedPropertiesValue;

        return value;

    }

 

    private static void AttachOrAddGraph(ObjectContext context, EntityObject entity, bool isNewEntity, IEnumerable<string> changedProperties, Func<ObjectContext> createContext)

    {

        var relatedEntities = entity.RelatedEntities().Where(e => e.Value.Length > 0).ToList();

        var relatedEntityKeys = new Dictionary<IRelatedEnd, EntityKey>();

        foreach (var relatedEndInfo in relatedEntities)

        {

            EntityReference entityReference;

            if ((entityReference = relatedEndInfo.Key as EntityReference) != null)

                relatedEntityKeys.Add(entityReference, entityReference.EntityKey);

            foreach (EntityObject relatedEntity in relatedEndInfo.Value)

                relatedEndInfo.Key.Remove(relatedEntity);

        }

 

        EntityObject entityObject = null;

        if (isNewEntity)

            context.AddObject(entity.EntitySetName(context), entity);

        else

        {

            entityObject = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged).Select(ose => ose.Entity).OfType<EntityObject>().Where(e => e.EntityKey == entity.EntityKey).FirstOrDefault();

            if (entityObject == null)

            {

                context.Attach(entity);

                var changedPropertiesEnumerator = changedProperties.GetEnumerator();

                if (changedPropertiesEnumerator.MoveNext())

                {

                    var ose = context.ObjectStateManager.GetObjectStateEntry(entity);

                    ose.SetModified();

                    do

                    {

                        ose.SetModifiedProperty(changedPropertiesEnumerator.Current);

                    } while (changedPropertiesEnumerator.MoveNext());

                }

            }

        }

 

        foreach (var relatedEndInfo in relatedEntities)

        {

            if (relatedEndInfo.Value.Any())

                foreach (EntityObject relatedEntity in relatedEndInfo.Value)

                {

                    bool relatedEntityIsNew;

                    relatedEntity.AttachGraph(context, createContext, out relatedEntityIsNew);

                    if (isNewEntity)

                        relatedEndInfo.Key.Add(relatedEntity);

                    else if (relatedEntityIsNew)

                    {

                        EntityReference entityReference;

                        if ((entityReference = relatedEndInfo.Key as EntityReference) != null)

                            using (var context2 = createContext())

                            {

                                AttachOldEntityReference(entity, context, entityReference, (EntityReference)((EntityObject)context2.GetObjectByKey(entity.EntityKey)).RelatedEnds().Where(re => re.RelationshipName == relatedEndInfo.Key.RelationshipName).First());

                            }

                        relatedEndInfo.Key.Add(relatedEntity);

                    }

                    else

                    {

                        using (var context2 = createContext())

                        {

                            var entity2 = (EntityObject)context2.GetObjectByKey(entity.EntityKey);

                            var relatedEnd2 = entity2.RelatedEnds().Where(re => re.RelationshipName == relatedEndInfo.Key.RelationshipName).First();

                            var association = context2.MetadataWorkspace.GetItemCollection(DataSpace.CSpace).OfType<AssociationType>().Where(at => at.FullName == relatedEndInfo.Key.RelationshipName).First();

                            bool relationExistsInDB;

                            if (association.AssociationEndMembers[0].RelationshipMultiplicity == RelationshipMultiplicity.Many && association.AssociationEndMembers[1].RelationshipMultiplicity == RelationshipMultiplicity.Many)

                            {

                                var entityCondition = GenerateESQLCondition(entity, "entity");

                                var relatedCondition = GenerateESQLCondition(relatedEntity, "relatedEntity");

                                var query = context2.CreateQuery<DbDataRecord>(string.Format("SELECT EXISTS(SELECT 1 FROM entity.{0} AS relatedEntity WHERE {1}) AS RelationExistInDB FROM {2}.{3} AS entity WHERE {4}", context2.MetadataWorkspace.GetItemCollection(DataSpace.CSpace).OfType<EntityType>().Where(at => at.Name == entity.GetType().Name).First().NavigationProperties.First(np => ((AssociationType)np.MetadataProperties.First(mp => mp.Name == "RelationshipType").Value).FullName ==

                                relatedEnd2.RelationshipName).Name, relatedCondition.Key, context2.DefaultContainerName, entity2.EntityKey.EntitySetName, entityCondition.Key), (entityCondition.Value.Union(relatedCondition.Value)).ToArray());

                                relationExistsInDB = query.First().GetBoolean(0);

                            }

                            else

                            {

                                EntityReference entityReference;

                                if ((entityReference = relatedEndInfo.Key as EntityReference) != null)

                                    AttachOldEntityReference(entity, context, entityReference, (EntityReference)relatedEnd2);

 

                                context2.GetObjectByKey(relatedEntity.EntityKey);

                                relationExistsInDB = relatedEnd2.GetContents().Any(rei => rei.EntityKey == relatedEntity.EntityKey);

                            }

 

                            if (relationExistsInDB)

                            {

                                if (entityObject == null)

                                    relatedEndInfo.Key.Attach(relatedEntity);

                                else

                                    entityObject.RelatedEnds().Where(re => re.RelationshipName == relatedEndInfo.Key.RelationshipName).First().Attach(relatedEntity);

                            }

                            else

                                relatedEndInfo.Key.Add(relatedEntity);

                        }

                    }

                }

            else

            {

                EntityReference entityReference;

                if (!isNewEntity && (entityReference = relatedEndInfo.Key as EntityReference) != null && entityReference.EntityKey == null)

                {

                    using (var context2 = createContext())

                    {

                        var entity2 = (EntityObject)context2.GetObjectByKey(entity.EntityKey);

                        var relatedEnd2 = (EntityReference)entity2.RelatedEnds().Where(re => re.RelationshipName == entityReference.RelationshipName).First();

                        AttachOldEntityReference(entity, context, entityReference, relatedEnd2);

                    }

                    EntityKey outReferenceEntityKey;

                    if (relatedEntityKeys.TryGetValue(relatedEndInfo.Key, out outReferenceEntityKey))

                        entityReference.EntityKey = outReferenceEntityKey;

                    else

                        entityReference.EntityKey = null;

                }

            }

        }

    }

 

    private static void AttachOldEntityReference(EntityObject entity, ObjectContext context, EntityReference entityReference, EntityReference entityReference2)

    {

        if (entityReference.EntityKey != entityReference2.EntityKey)

        {

            int associationAdded = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Where(ose => ose.EntitySet is AssociationSet).Count();

            entityReference.EntityKey = entityReference2.EntityKey;

            var q = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Where(ose => ose.EntitySet is AssociationSet);

            if (q.Count() != associationAdded)

                q.Last().AcceptChanges();

        }

    }

 

    private static KeyValuePair<string, List<ObjectParameter>> GenerateESQLCondition(EntityObject entity, string parameterName)

    {

        string value = null;

        var whereParameters = new List<ObjectParameter>();

        foreach (var entityKey in entity.EntityKey.EntityKeyValues)

        {

            var whereParameterName = string.Concat(parameterName, entityKey.Key);

            var condition = string.Format("{0}.{1} = @{2}", parameterName, entityKey.Key, whereParameterName);

            whereParameters.Add(new ObjectParameter(whereParameterName, entityKey.Value));

            if (value == null)

                value = condition;

            else

                value = string.Concat(" AND ", condition);

        }

        return new KeyValuePair<string, List<ObjectParameter>>(value, whereParameters);

    }

 

    private static IEnumerable<KeyValuePair<IRelatedEnd, EntityObject[]>> RelatedEntities(this EntityObject entity)

    {

        foreach (var relatedEnd in entity.RelatedEnds())

            yield return new KeyValuePair<IRelatedEnd, EntityObject[]>(relatedEnd, relatedEnd.GetContents().ToArray());

    }

 

    private static IEnumerable<IRelatedEnd> RelatedEnds(this EntityObject entity)

    {

        foreach (var relatedEnd in ((IEntityWithRelationships)entity).RelationshipManager.GetAllRelatedEnds())

            yield return relatedEnd;

    }

 

    private static IEnumerable<EntityObject> GetContents(this IRelatedEnd relatedEnd)

    {

        foreach (object entity in relatedEnd)

            yield return entity as EntityObject;

    }

 

    private static string EntitySetName(this EntityObject entity, ObjectContext context)

    {

        if (entity.EntityKey != null)

            return entity.EntityKey.EntitySetName;

        return context.MetadataWorkspace.GetItemCollection(DataSpace.CSpace).OfType<EntityContainer>().First().BaseEntitySets.First(es =>

        {

            var entityTypeMetadata = context.MetadataWorkspace.GetItemCollection(DataSpace.CSpace).OfType<EdmType>().First(ese => ese.Name == entity.GetType().Name);

            while (entityTypeMetadata.BaseType != null)

                entityTypeMetadata = entityTypeMetadata.BaseType;

            return es.ElementType == entityTypeMetadata;

        }).Name;

    }

}

I want to do a big big big thanks to Jeff for all his advices.

How to solve a big EDM performance problem?

Last week, a customer asked me how to solve a big EDM performance problem? In his case, his model was mapped on around 700 tables. I am amused and happy to see that I proposed what Srikanth proposed yesterday:

  • Do distinct model for unlinked tables
  • Break the relation in the EDM

Go back on this last point. Imagine you have a Category module and a Product module (using Northwind). It is possible to have one EDM per module. So we will have an int CategoryID property on Product entity type.

If you want to add or modify a product, you will probably use a combobox (or another selection control) to choose the category. This scenario isn’t a problem. Indeed, you just need to set combo.DataSource with the categories, the ValueMember with CategoryID and the DisplayMember with CategoryName. 

Now what happens if you want to create a form which creates a category and a product together? No problem: you will add the category with the category module which will return the CategoryID and then we will add the new product with the CategoryID. Now we miss something: the transaction. Indeed, when you do a SaveChanges, a transaction is automatically created. In this case, there are two different SaveChanges. If you want to cancel the category creation if the product creation failed, we can use a transactionscope around the two creations.
In my opinion, I find absurd to have so many entity types in an EDM. Indeed, it will be very difficult to maintain it because of its size. Moreover, conceptually, we often use module architecture so it makes sense to do the same with EDM.

A last point: it is possible to have entity types mapped on the same table and the same columns in two different EDM. Imagine that you want to be able to get the categories which have any products in the category module. You can use an SSDL View (which can or cannot be a view in your DB) but you can also integrate the product entity type in your category EDM. If you only want to use it to get the categories with products, it is possible to define the product table with only two columns (in your SSDL): ProductID and CategoryID. Doing this reduces the flow with your DB to get only what you really need. However, be careful as doing this generates an important constraint: in DB, ProductName doesn’t allow null value. This means that if you try to add a product in the category EDM, you will get an exception on the SaveChanges. But this can be an advantage because you will impose to use the product EDM which is done for this.

How to simulate a 1 to 0..1 relationship when you must have a 1 to 1

If you take the Noam edm, you have a 1 to 1 relationship that you must have because the relationship is on the PK of the two entities which are the same.

Now imagine that in his sample, FrontImage, TopImage and SideImage are nullable.

We can imagine creating a new Product without a new ProductImages.

If you do this, you will have an exception on the SaveChanges because you have a 1 to 1 relationship so you must have a ProductImages associated.

My idea is to automaticaly create the ProductImages (if it does'nt exist) on the SaveChanges.

What I suggest is to extend the ObjectContext, to use the partial method OnContextCreated to add handler to the SavingChanges event:

partial class Test4125924Entities

{

    partial void OnContextCreated()

    {

        SavingChanges += (sender, e) =>

        {

            foreach (var product in ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(ose => ose.Entity).OfType<Product>())

                if (product.ProductImages == null)

                    product.ProductImages = new ProductImages();

        };

    }

}

Posted by Matthieu MEZIL | with no comments

How to split a data table?

In this msdn thread, zeeshan hirani wants to split a data table in two entities to do a sort of delay loading?

So how to do this?

I do this with Northwind:

<!-- EF Runtime content -->

<edmx:Runtime>

    <!-- SSDL content -->

    <edmx:StorageModels>

        <Schema Namespace="NorthwindModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">

            <EntityContainer Name="NorthwindModelStoreContainer">

                <EntitySet Name="Categories" EntityType="NorthwindModel.Store.Categories" store:Type="Tables" Schema="dbo" />

            </EntityContainer>

            <EntityType Name="Categories">

                <Key>

                    <PropertyRef Name="CategoryID" />

                </Key>

                <Property Name="CategoryID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />

                <Property Name="CategoryName" Type="nvarchar" Nullable="false" MaxLength="15" />

                <Property Name="Description" Type="nvarchar" MaxLength="200" />

                <Property Name="Picture" Type="image" />

            </EntityType>

        </Schema>

    </edmx:StorageModels>

    <!-- CSDL content -->

    <edmx:ConceptualModels>

        <Schema Namespace="NorthwindModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">

            <EntityContainer Name="NorthwindEntities">

                <EntitySet Name="CategorySet" EntityType="NorthwindModel.Category" />

                <EntitySet Name="CategoryPictureSet" EntityType="NorthwindModel.CategoryPicture" />

                <AssociationSet Name="CategoryCategoryPicture" Association="NorthwindModel.CategoryCategoryPicture">

                    <End Role="Category" EntitySet="CategorySet" />

                    <End Role="CategoryPicture" EntitySet="CategoryPictureSet" />

                </AssociationSet>

            </EntityContainer>

            <EntityType Name="Category">

                <Key>

                    <PropertyRef Name="CategoryID" />

                </Key>

                <Property Name="CategoryID" Type="Int32" Nullable="false" />

                <Property Name="CategoryName" Type="String" Nullable="false" MaxLength="15" Unicode="true" FixedLength="false" />

                <Property Name="Description" Type="String" MaxLength="200" Unicode="true" FixedLength="false" />

                <NavigationProperty Name="Picture" Relationship="NorthwindModel.CategoryCategoryPicture" FromRole="Category" ToRole="CategoryPicture" a:SetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" />

            </EntityType>

            <EntityType Name="CategoryPicture">

                <Key>

                    <PropertyRef Name="CategoryID" />

                </Key>

                <Property Name="CategoryID" Type="Int32" Nullable="false" a:GetterAccess="Private" xmlns:a="http://schemas.microsoft.com/ado/2006/04/codegeneration" a:SetterAccess="Private" />

                <NavigationProperty Name="Category" Relationship="NorthwindModel.CategoryCategoryPicture" FromRole="CategoryPicture" ToRole="Category" />

                <Property Name="Picture" Type="Binary" Nullable="true" />

            </EntityType>

            <Association Name="CategoryCategoryPicture">

                <End Type="NorthwindModel.Category" Role="Category" Multiplicity="1" />

                <End Type="NorthwindModel.CategoryPicture" Role="CategoryPicture" Multiplicity="1" />

                <ReferentialConstraint>

                    <Principal Role="Category">

                        <PropertyRef Name="CategoryID" />

                    </Principal>

                    <Dependent Role="CategoryPicture">

                        <PropertyRef Name="CategoryID" />

                    </Dependent>

                </ReferentialConstraint>

            </Association>

        </Schema>

    </edmx:ConceptualModels>

    <!-- C-S mapping content -->

    <edmx:Mappings>

        <Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">

            <EntityContainerMapping StorageEntityContainer="NorthwindModelStoreContainer" CdmEntityContainer="NorthwindEntities">

                <EntitySetMapping Name="CategorySet">

                    <EntityTypeMapping TypeName="IsTypeOf(NorthwindModel.Category)">

                        <MappingFragment StoreEntitySet="Categories">

                            <ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

                            <ScalarProperty Name="CategoryName" ColumnName="CategoryName" />

                            <ScalarProperty Name="Description" ColumnName="Description" />

                        </MappingFragment>

                    </EntityTypeMapping>

                </EntitySetMapping>

                <AssociationSetMapping Name="CategoryCategoryPicture" TypeName="NorthwindModel.CategoryCategoryPicture" StoreEntitySet="Categories">

                    <EndProperty Name="CategoryPicture">

                        <ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

                    </EndProperty>

                    <EndProperty Name="Category">

                        <ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

                    </EndProperty>

                </AssociationSetMapping>

                <EntitySetMapping Name="CategoryPictureSet">

                    <EntityTypeMapping TypeName="IsTypeOf(NorthwindModel.CategoryPicture)">

                        <MappingFragment StoreEntitySet="Categories">

                            <ScalarProperty Name="Picture" ColumnName="Picture" />

                            <ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

                        </MappingFragment>

                    </EntityTypeMapping>

                </EntitySetMapping>

            </EntityContainerMapping>

        </Mapping>

    </edmx:Mappings>

</edmx:Runtime>

 

Then, I extend (partial) the CategoryPicture class to do a constructor with a Category parameter. Indeed, we can’t create a CategoryPicture without a Category because CategoryName doesn’t allow null.

partial class CategoryPicture

{

    public CategoryPicture(Category category)

    {

        Category = category;

        CategoryID = category.CategoryID;

    }

}

How to dress up to a wedding?

When you are invited to a wedding, you have to be fashionable. But if the groom works in computing, an important grand shirt is a little too conservative and the best is this:

Posted by Matthieu MEZIL | with no comments
Filed under:

Get added entities in the query (next)

I want to complete my yesterday post.

If you just want to get the entities Count, it is useless to load all the entities in memory.

So in this case, it's better to do something like this:

context.MyEntitySet.Count() + context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(e => e.Entity).OfType<MyEntityType>().Count() - context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).Select(e => e.Entity).OfType<MyEntityType>().Count()

Posted by Matthieu MEZIL | with no comments

C# compiler and implicit keyword: can be better

Imagine the following code:

class A

{

    public static implicit operator B(A a)

    {

        return new B(a);

    }

}

class B

{

    public B(A a)

    {

    }

}

This code compile without error and it's normal.

Now imagine the following one:

class A

{

    public static implicit operator B(A a)

    {

        return new A();

    }

}

The compiler generates no error with this code but it should!

Less annoying, the compiler generates an error with this code (Cannot implicitly convert type 'A' to 'C') even though it would be able to use B to convert A to C:

class Program

{

    static void Main(string[] args)

    {

        C c = new A();

    }

}

 

class A

{

    public static implicit operator B(A a)

    {

        return new B(a);

    }

}

class B

{

    public B(A a)

    {

    }

 

    public static implicit operator C(B b)

    {

        return new C(b);

    }

}

 

class C

{

    public C(B b)

    {

    }

}

Posted by Matthieu MEZIL | with no comments
Filed under: , ,

Get added entities in the query

I had the following question:

Why, when we add an entity in an ObjectContext, don't we get it from a query until we save the ObjectContext?

For example,

context.MyEntities.Count returns n.

Now, if we add a new entity in the context, context.MyEntities.Count still returns n, not n + 1.

In fact, when we execute a query, the query is translated into a sql query and so, the result is the result of the database and until you call the SaveChanges method on the context, the Added or Deleted entities are ignored.

So how to consider the EntityState?

We can use the ObjectStateManager.

You have to load all the entities you need from the DB to have them in the ObjectContext's ObjectStateManager.

Then, you can get the entities with:

context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged).Select(e => e.Entity).OfType<MyEntityType>()