Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Abonnements

Entités Self-Tracking : Comment réduire le flux entre le client et le serveur ?

Les entités Self-Tracking sont vraiment une bonne chose pour les scenarii N-Tiers mais il faut faire attention si on veut réduire l’échange entre le serveur et le client.

Prenons l’EDM suivant :

image

Côté client, je veux récupérer un graphe très important d’entités, modifier une propriété d’une seule entité et persister mes modifications :

using (var context = new NorthwindClientContext())
{
    var c = context.Categories.AsQueryable().Include("Products.OrderDetails.Order.Employee").Include("Products.OrderDetails.Order.Customer.CustomerDemographics").Include("Products.OrderDetails.Order.Customer.member").First();
    var p = c.Products.First();
    var pName = p.ProductName;
    p.ProductName = "azerty";
    context.SaveChanges();
}

Pour persister l’ensemble des modifications de mon contexte client, je passe par une classe ClientContext qui contient une collection de chacun des types de mes EntitySets :

ClientContext SaveChanges(ClientContext context);

[DataContract]
public class ClientContext
{
       [DataMember]
       public List<Customer> Customers { get; set; }
       [DataMember]
       public List<OrderDetail> OrderDetailSet { get; set; }
       [DataMember]
       public List<Order> Orders { get; set; }
       [DataMember]
       public List<Product> Products { get; set; }
       [DataMember]
       public List<CustomerDemographic> CustomerDemographics { get; set; }
       [DataMember]
       public List<Category> Categories { get; set; }
       [DataMember]
       public List<Employee> Employees { get; set; }
       [DataMember]
       public List<Member> Members { get; set; }
}

A noter que le résultat de type ClientContext est nécessaire pour les colonnes Identity et Computed.

Cependant, l’idée n’est pas d’échanger l’ensemble des entités pour une petite modification sur une seule propriété d’une seule entité.

Ma première idée a été de réduire le ClientContext envoyé du client vers le serveur :

var clientContext = new ClientContext

       Customers = 
              (from e in Customers.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList(), 
       OrderDetailSet = 
              (from e in OrderDetailSet.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList(), 
       Orders = 
              (from e in Orders.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList(), 
       Products = 
              (from e in Products.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList(), 
       CustomerDemographics = 
              (from e in CustomerDemographics.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList(), 
       Categories = 
              (from e in Categories.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList(), 
       Employees = 
              (from e in Employees.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList(), 
       Members = 
              (from e in Members.AllEntities
               where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
               select e).ToList()
};

Dans les faits, cela n’est pas efficace car la seule entité modifiée l’est avec son graphe complet, ce qui dans notre cas implique l’ensemble des entités chargées dans le contexte client.

Dans mes templates, dont je vous parle depuis plusieurs posts, j’ai réduit l’échange entre le client et le serveur au strict minimum : Product { ProductID = 1, ProductName = “azerty” }. Comment ais-je fait cela ?

Premièrement, J’ai ajouté une propriété ModifiedProperties (List<string>) sur la classe ObjectChangeTracker. Ensuite dans mon SaveChanges côté client, j’ai ajouté le code suivant :

var sentContext = new ClientContext();
sentContext.Customers = 
       (from e in clientContext.Customers
        select ReduceToModifications(e)).ToList();
sentContext.OrderDetailSet = 
       (from e in clientContext.OrderDetailSet
        select ReduceToModifications(e)).ToList();
sentContext.Orders = 
       (from e in clientContext.Orders
        select ReduceToModifications(e)).ToList();
sentContext.Products = 
       (from e in clientContext.Products
        select ReduceToModifications(e)).ToList();
sentContext.CustomerDemographics = 
       (from e in clientContext.CustomerDemographics
        select ReduceToModifications(e)).ToList();
sentContext.Categories = 
       (from e in clientContext.Categories
        select ReduceToModifications(e)).ToList();
sentContext.Employees = 
       (from e in clientContext.Employees
        select ReduceToModifications(e)).ToList();
sentContext.Members = 
       (from e in clientContext.Members 
        select ReduceToModifications(e)).ToList();

Pour le cas de Product, la méthode reduceToModifications est la suivant :

private Product ReduceToModifications(Product entity)
{
    Product value = new Product { ProductID = entity.ProductID };
    value.ChangeTracker.ChangeTrackingEnabled = true;
    value.ChangeTracker.State = entity.ChangeTracker.State;
    switch (entity.ChangeTracker.State)
    {
        case ObjectState.Added:
            value.ProductName = entity.ProductName;
            value.SupplierID = entity.SupplierID;
            value.CategoryID = entity.CategoryID;
            value.QuantityPerUnit = entity.QuantityPerUnit;
            value.UnitPrice = entity.UnitPrice;
            value.UnitsInStock = entity.UnitsInStock;
            value.UnitsOnOrder = entity.UnitsOnOrder;
            value.ReorderLevel = entity.ReorderLevel;
            value.Discontinued = entity.Discontinued;
            break;
        case ObjectState.Deleted:
            break;
        case ObjectState.Modified:
            value.ChangeTracker.ModifiedProperties = entity.ChangeTracker.ModifiedProperties;
            foreach (var modifiedPropery in entity.ChangeTracker.ModifiedProperties)
                // switch is more efficient than reflection
                switch (modifiedPropery)
                {
                    case "ProductName":
                        value.ProductName = entity.ProductName;
                        break;
                    case "SupplierID":
                        value.SupplierID = entity.SupplierID;
                        break;
                    case "CategoryID":
                        value.CategoryID = entity.CategoryID;
                        break;
                    case "QuantityPerUnit":
                        value.QuantityPerUnit = entity.QuantityPerUnit;
                        break;
                    case "UnitPrice":
                        value.UnitPrice = entity.UnitPrice;
                        break;
                    case "UnitsInStock":
                        value.UnitsInStock = entity.UnitsInStock;
                        break;
                    case "UnitsOnOrder":
                        value.UnitsOnOrder = entity.UnitsOnOrder;
                        break;
                    case "ReorderLevel":
                        value.ReorderLevel = entity.ReorderLevel;
                        break;
                    case "Discontinued":
                        value.Discontinued = entity.Discontinued;
                        break;
                    case "OrderDetails":
                        value.OrderDetails = entity.OrderDetails;
                        break;
 
                   case "Category":
                       value.Category = entity.Category;
                        break;
                }
            break;
        case ObjectState.Unchanged:
            break;
        default:
            throw new InvalidOperationException();
    }
    return value;
}

Ensuite, je dois jouer avec les propriétés ObjectsAddedToCollectionProperties, ObjectsRemovedFromCollectionProperties et OriginalValues pour les relations. Sans ça, on ne peut pas reporter les relations many to many et on ne peut pas être sûr de l’ordre des commandes SQL ce qui peut entrainer des exceptions (par violation “temporaire” des FK).

Au final ma méthode SaveChanges est la suivante :

public void SaveChanges()
{
       var clientContext = new ClientContext 
      
              Customers = 
                     (from e in Customers.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList(), 
              OrderDetailSet = 
                     (from e in OrderDetailSet.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList(), 
              Orders = 
                     (from e in Orders.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList(), 
              Products = 
                     (from e in Products.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList(), 
              CustomerDemographics = 
                     (from e in CustomerDemographics.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList(), 
              Categories = 
                     (from e in Categories.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList(), 
              Employees = 
                     (from e in Employees.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList(), 
              Members = 
                     (from e in Members.AllEntities
                      where e.ChangeTracker.State != ObjectState.Unchanged || e.ChangeTracker.ObjectsAddedToCollectionProperties.Any() || e.ChangeTracker.ObjectsRemovedFromCollectionProperties.Any()
                      select e).ToList() 
       };

      
var sentContext = new ClientContext();
       sentContext.Customers = 
              (from e in clientContext.Customers
               select ReduceToModifications(e)).ToList();
       sentContext.OrderDetailSet = 
              (from e in clientContext.OrderDetailSet
               select ReduceToModifications(e)).ToList();
       sentContext.Orders = 
              (from e in clientContext.Orders
               select ReduceToModifications(e)).ToList();
       sentContext.Products = 
              (from e in clientContext.Products
               select ReduceToModifications(e)).ToList();
       sentContext.CustomerDemographics = 
              (from e in clientContext.CustomerDemographics
               select ReduceToModifications(e)).ToList();
       sentContext.Categories = 
              (from e in clientContext.Categories
               select ReduceToModifications(e)).ToList();
       sentContext.Employees = 
              (from e in clientContext.Employees
               select ReduceToModifications(e)).ToList();
       sentContext.Members = 
              (from e in clientContext.Members
               select ReduceToModifications(e)).ToList();  

       int nbCustomers = sentContext.Customers.Count;
       for (int index = 0 ; index < nbCustomers ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.Customers[index], sentContext.Customers[index]);
       int nbOrderDetailSet = sentContext.OrderDetailSet.Count;
       for (int index = 0 ; index < nbOrderDetailSet ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.OrderDetailSet[index], sentContext.OrderDetailSet[index]);
       int nbOrders = sentContext.Orders.Count;
       for (int index = 0 ; index < nbOrders ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.Orders[index], sentContext.Orders[index]);
       int nbProducts = sentContext.Products.Count;
       for (int index = 0 ; index < nbProducts ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.Products[index], sentContext.Products[index]);
       int nbCustomerDemographics = sentContext.CustomerDemographics.Count;
       for (int index = 0 ; index < nbCustomerDemographics ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.CustomerDemographics[index], sentContext.CustomerDemographics[index]);
       int nbCategories = sentContext.Categories.Count;
       for (int index = 0 ; index < nbCategories ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.Categories[index], sentContext.Categories[index]);
       int nbEmployees = sentContext.Employees.Count;
       for (int index = 0 ; index < nbEmployees ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.Employees[index], sentContext.Employees[index]);
       int nbMembers = sentContext.Members.Count;
       for (int index = 0 ; index < nbMembers ; index ++)
              ReduceNavigationProperties(sentContext, clientContext.Members[index], sentContext.Members[index]); 

       Refresh(clientContext, base.Channel.SaveChanges(sentContext));

}

Pour la classe Product, la méthode ReduceNavigationProperties est la suivante :

private void ReduceNavigationProperties(ClientContext context, Product originalValue, Product newValue)
{
    foreach (var relatedEntity in originalValue.ChangeTracker.OriginalValues)
    {
        switch (relatedEntity.Key)
        {
            case "Category":
                var categoryParentEntity = (Category)relatedEntity.Value;
                var newCategoryParentEntity = context.Categories.First(e => e.Id == categoryParentEntity.Id);
                newValue.ChangeTracker.OriginalValues.Add("Category", newCategoryParentEntity);
                ObjectList categoryParentEntityObjectList;
                if (!newCategoryParentEntity.ChangeTracker.ObjectsRemovedFromCollectionProperties.TryGetValue("Products", out categoryParentEntityObjectList))
                {
                    categoryParentEntityObjectList = new ObjectList();
                    newCategoryParentEntity.ChangeTracker.ObjectsRemovedFromCollectionProperties.Add("Products", categoryParentEntityObjectList);
                }
                categoryParentEntityObjectList.Add(newValue);
                newValue.CategoryID = originalValue.CategoryID;
                break;
        }
    }
    switch (originalValue.ChangeTracker.State)
    {

        case ObjectState.Added:
        case ObjectState.Deleted:
            foreach (var subEntity in originalValue.OrderDetails.Where(se => se.ChangeTracker.State != ObjectState.Unchanged))
            {
                var relatedEntity = context.OrderDetailSet.First(e => e.OrderID == subEntity.OrderID && e.ProductID == subEntity.ProductID);
                if (! newValue.OrderDetails.Contains(relatedEntity))
                    newValue.OrderDetails.Attach(relatedEntity);
            }
            if (originalValue.Category != null && originalValue.ChangeTracker.State == ObjectState.Unchanged)
            {
                var relatedEntity = context.Categories.First(e => e.Id == originalValue.Category.Id);
                if (newValue.Category != relatedEntity)
                    newValue.Category = relatedEntity;
            }
            break;
    }
}

Avec ce code (entièrement généré avec T4), le flux entre le client et le serveur est réduit au minimum.

Ensuite côté client, nous utilisons ModifiedProperties pour reportés les modifications (comme décrit dans mon dernier post).

A la place de :

context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);

J’utilise le code suivant :

context.ObjectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
var ose = context.ObjectStateManager.GetObjectStateEntry(entity);
ose.SetModified();
foreach (var propertyName in entity.ChangeTracker.ModifiedProperties) 
    ose.SetModifiedProperty(propertyName);

Du coup dans le cadre de mon exemple, l’UPDATE SQL se limite aux colonnes réellement modifiées:

exec sp_executesql N'update [dbo].[Products]
set [ProductName] = @0
where ([ProductID] = @1)
',N'@0 nvarchar(40),@1 int',@0=N'azerty',@1=1

Il reste à traiter la méthode Refresh. Dans celle-ci, il faut raffraîchir les propriétés de type Identity (pour l’ajout), Computed Properties pour le Add et l’Update, et les FK de type Identity quand l’entité liée est dans l’état Added. Il est impératif de se réduire à ces modifications parce que dans la mesure où on s’est limité à un clone réduit de l’entité les autres propriétés n’ayant aucune raison d’être renseignées côté serveur, elles garderont leur valeur par défaut.

Voici la méthode Refresh :

private void Refresh(ClientContext clientContext, ClientContext dbContext)
{
    int customersCount = clientContext.Customers.Count;
    for (int i = 0 ; i < customersCount ; i ++)
    {
        var clientEntity = clientContext.CustomersIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            Customers.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.CustomersIdea;
        RefreshComputedValues(clientEntity, dbEntity);
    }
    int orderDetailSetCount = clientContext.OrderDetailSet.Count;
    for (int i = 0 ; i < orderDetailSetCount ; i ++)
    {
        var clientEntity = clientContext.OrderDetailSetIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            OrderDetailSet.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.OrderDetailSetIdea;
        RefreshComputedValues(clientEntity, dbEntity);
    }
    int ordersCount = clientContext.Orders.Count;
    for (int i = 0 ; i < ordersCount ; i ++)
    {
        var clientEntity = clientContext.OrdersIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            Orders.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.OrdersIdea;
        RefreshComputedValues(clientEntity, dbEntity);
    }
    int productsCount = clientContext.Products.Count;
    for (int i = 0 ; i < productsCount ; i ++)
    {
        var clientEntity = clientContext.ProductsIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            Products.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.ProductsIdea;
        RefreshComputedValues(clientEntity, dbEntity);
    }
    int customerDemographicsCount = clientContext.CustomerDemographics.Count;
    for (int i = 0 ; i < customerDemographicsCount ; i ++)
    {
        var clientEntity = clientContext.CustomerDemographicsIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            CustomerDemographics.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.CustomerDemographicsIdea;
        RefreshComputedValues(clientEntity, dbEntity);
    }
    int categoriesCount = clientContext.Categories.Count;
    for (int i = 0 ; i < categoriesCount ; i ++)
    {
        var clientEntity = clientContext.CategoriesIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            Categories.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.CategoriesIdea;
        RefreshComputedValues(clientEntity, dbEntity);
    }
    int employeesCount = clientContext.Employees.Count;
    for (int i = 0 ; i < employeesCount ; i ++)
    {
        var clientEntity = clientContext.EmployeesIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            Employees.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.EmployeesIdea;
        bool typeFound = false;
        var clientEntityAsEmployeeInActivity = clientEntity as EmployeeInActivity;
        if (clientEntityAsEmployeeInActivity != null)
        {
            RefreshComputedValues(clientEntityAsEmployeeInActivity, (EmployeeInActivity)dbEntity);
            typeFound = true;
        }
        var clientEntityAsFiredEmployee = clientEntity as FiredEmployee;
        if (clientEntityAsFiredEmployee != null)
        {
            RefreshComputedValues(clientEntityAsFiredEmployee, (FiredEmployee)dbEntity);
            typeFound = true;
        }
        var clientEntityAsOutEmployee = clientEntity as OutEmployee;
        if (clientEntityAsOutEmployee != null)
        {
            RefreshComputedValues(clientEntityAsOutEmployee, (OutEmployee)dbEntity);
            typeFound = true;
        }
        if (! typeFound)
            RefreshComputedValues(clientEntity, dbEntity);
    }
    int membersCount = clientContext.Members.Count;
    for (int i = 0 ; i < membersCount ; i ++)
    {
        var clientEntity = clientContext.MembersIdea;
        if (clientEntity.ChangeTracker.State == ObjectState.Deleted)
        {
            Members.Detach(clientEntity);
            continue;
        }
        var dbEntity = dbContext.MembersIdea;
        RefreshComputedValues(clientEntity, dbEntity);
    }
}

La méthode RefreshComputedValues pour Product est la suivante :

private void RefreshComputedValues(Product entity, Product dbEntity)
{
    if (dbEntity.ChangeTracker.State == ObjectState.Added)
    {
        entity.ProductID = dbEntity.ProductID;
    }
    entity.IsDeserializing = true;
    if (dbEntity.Category != null && dbEntity.Category.ChangeTracker.State == ObjectState.Added)
        entity.CategoryID = dbEntity.CategoryID;
    entity.IsDeserializing = false;
    entity.ChangeTracker.AcceptChanges();
}

Je sais, je vais me répéter mais ce qui est vraiment cool dans mon approche est le fait que tout ce code est entièrement généré. Quand mon modèle d’entité est défini, je peux générer la base et je peux générer la plus grosse partie de mon code. De plus, mes templates générant des classes / interfaces partielles avec parfois des méthodes partielles je peux les étendre sans problème afin de rajouter du code spécifique (pour la logique métier par exemple). La seule chose qu’il me reste ensuite à coder est la couche de présentation.

Outre la productivité, il y a un autre intérêt de passer par T4. Vous avez pu remarquer dans mon précédent post que j’avais fait pas mal de tests sur le code généré à partir de mes T4. // Soit dit en passant j’ai encore augmenté le nombre de mes tests.
Sans m’engager sur le bug free, cela m’assure quand même une importante réduction du risque.
De plus et surtout, si je n’utilisais pas de template de génération de code, il me faudrait tester les relations one to many (un exemple parmi tant d’autre) pour Category / Product, Product / OrderDetail, Employee / Order, Customer / Order, Order / OrderDetail, etc. Avec du code écrit à la main on n’est, en effet, jamais à l’abri de l’erreur humaine (le copier coller foireux par exemple) et il n’est pas toujours possible / conseillé de tout factoriser. Avec du code généré, une fois qu’on a validé le bon fonctionnement des relations one to many, on n’a plus besoin de s’en soucier et ce, indépendamment des entités et indépendamment du projet sur lequel on travaille.

Welcome to the data driven world! Smile

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Publié jeudi 24 décembre 2009 02:45 par Matthieu MEZIL

Commentaires

# re: Entités Self-Tracking : Comment réduire le flux entre le client et le serveur ? @ mercredi 10 février 2010 22:27

Bonjour,

Bonne idee que d'essayer de reduire le volume des echanges, mais dans l'exemple on ne mets a jour que les colonnes modifiées sans ce soucier de savoir si une autre modif a eu lieu entre le moment ou l'enregistrement a ete pris et le moment de la mise a jour. Qui nous dit que la colonne mis a jour n'est pas lié à une autre d'un point de vue fonctionnel...  Il nous manque quelquechose pour gerer le versionning de la ligne (a la sauce nhibernate via une colonne technique  ou autre)

Xrev

# re: Entités Self-Tracking : Comment réduire le flux entre le client et le serveur ? @ jeudi 11 février 2010 22:03

On a ça avec EF : avec la propriété ConcurrencyMode. Dans mon cas, les colonnes mappées sur des propriétés ConcurrencyMode = Fixed sont systématiquement incluses dans la query. De même, les colonnes de style Identity pour l'insert et Computed pour l'insert et l'update sont systématiquement récupérées.

Je ne sais pas si j'ai pensé à tout mais le fait de faire du TDD m'a aidé à m'en approcher. Smile

Matthieu MEZIL

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01