Avoiding Duplicates using Entity Framework

.net c# entity-framework linq

Question

Let's say I have the conceptual model shown below. There are stars with tags (more than one, so it's a many-to-many connection), and each tag is associated with a certain category.

I want to make sure that no duplicate tags are applied before inputting my data since it comes from an outside source.

Updated bit of code:

static void Main(string[] args)
    {
        Story story1 = new Story();
        story1.Title = "Introducing the Entity Framework";
        story1.Tags.Add(new Tag { Name = ".net",  });
        story1.Tags.Add(new Tag { Name = "database" });

        Story story2 = new Story();
        story2.Title = "Working with Managed DirectX";
        story2.Tags.Add(new Tag { Name = ".net" });
        story2.Tags.Add(new Tag { Name = "graphics" });

        List<Story> stories = new List<Story>();
        stories.Add(story1);
        stories.Add(story2);

        EfQuestionEntities db = new EfQuestionEntities();

        Category category = (from c in db.Categories
                             where c.Name == "Programming"
                             select c).First();

        foreach (Story story in stories)
        {
            foreach (Tag tag in story.Tags)
            {
                Tag currentTag = tag;
                currentTag = GetTag(tag.Name, category, db);
            }

            db.Stories.AddObject(story);
        }

        db.SaveChanges();
    }

    public static Tag GetTag(string name, Category category, EfQuestionEntities db)
    {
        var dbTag = from t in db.Tags.Include("Category")
                    where t.Name == name
                    select t;

        if (dbTag.Count() > 0)
        {
            return dbTag.First();
        }

        var cachedTag = db.ObjectStateManager.GetObjectStateEntries(EntityState.Added).
            Where(ose => ose.EntitySet == db.Tags.EntitySet).
            Select(ose => ose.Entity).
            Cast<Tag>().Where(x => x.Name == name);

        if (cachedTag.Count() != 0) 
        {
            return cachedTag.First();
        }

        Tag tag = new Tag();
        tag.Name = name;
        tag.Category = category;

        db.Tags.AddObject(tag);

        return tag;
    }

However, I see an error about a duplicate object in the ObjectContext that has the same EntityKey.

Additionally, removing the else sentence causes me to get an error for breaking an FK constraint, suggesting that the Category property may already be changed to null.

1
7
3/21/2011 1:18:13 PM

Accepted Answer

I had the similar issue with EF. Here is what I ultimately did:

  1. rather than doingstory1.Tags.Add(new Tag { Name = ".net", }) you routed everyoneTag creation using a helper approach such as this:story1.Tags.Add(GetTag(".net")) .
  2. The GetTag Like you do, method determines if it should return an existent object by looking at the tags in the context. If so, it gives it back.
  3. If no existing entity exists, it looks at theObjectStateManager to determine whether thereTag Entities introduced into the context but not yet recorded in the database. If it discovers a matchTag it gives back that.
  4. If it has not yet located theTag it makes a new one,Tag returns it after adding it to the context.

Basically, this will ensure that there is never more than one occurrence of anyTag (whether it is newly generated or already exists) will be utilized throughout your software.

a few lines of sample code from my appInventoryItem in place ofTag nonetheless, you get the point).

Step 3's check is carried out as follows:

// Second choice: maybe it's not in the database yet, but it's awaiting insertion?
inventoryItem = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
    .Where(ose => ose.EntitySet == context.InventoryItems.EntitySet)
    .Select(ose => ose.Entity)
    .Cast<InventoryItem>()
    .Where(equalityPredicate.Compile())
    .SingleOrDefault();

if (inventoryItem != null) {
    return inventoryItem;
}

If theTag is missing from step 3, here is the code:

inventoryItem = new InventoryItem();
context.InventoryItems.AddObject(inventoryItem);
return inventoryItem;

Update:

It should be used as follows:

Story story1 = new Story();
story1.Title = "Introducing the Entity Framework";
story1.Tags.Add(GetTag(".net", category, db));
story1.Tags.Add(GetTag("database", category, db));
4
3/21/2011 1:25:01 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow