简体   繁体   English

实体框架-更新没有ID的实体

[英]Entity Framework - Update entities without Id

I have found many questions here on SO and articles all over the internet but none really tackled my problem. 我在互联网上的SO和文章上发现了很多问题,但没有一个能真正解决我的问题。

My model looks like this (I striped all non essential Properties): 我的模型如下所示(我对所有非必要的属性进行了划分): 替代文字

Everyday or so "Play" gets updated (via a XML-file containing the information). 每天左右,“播放”都会更新(通过包含该信息的XML文件)。

internal Play ParsePlayInfo(XDocument doc)
{
    Play play = (from p in doc.Descendants("Play")
    select new Play
    {
        Theatre = new Theatre()
        {
            //Properties
        },
        //Properties
        LastUpdate = DateTime.Now
    }).SingleOrDefault();

    var actors = (from a in doc.XPathSelectElement(".//Play//Actors").Nodes()
    select new Lecturer()
    {
        //Properties
    });

    var parts = (from p in doc.XPathSelectElement(".//Play//Parts").Nodes()
    select new Part()
    {
        //Properties
    }).ToList();

    foreach (var item in parts)
    {
        play.Parts.Add(item);
    }

    var reviews = (from r in doc.XPathSelectElement(".//Play//Reviews").Nodes()
    select new Review
    {
        //Properties
    }).ToList();

    for (int i = 0; i < reviews.Count(); i++)
    {
        PlayReviews pR = new PlayReviews()
        {
            Review = reviews[i],
            Play = play,
            //Properties
        };
        play.PlayReviews.Add(pR);
    }
    return play;
}

If I add this "play" via Add() every Childobject of Play will be inserted - regardless if some exist already. 如果我通过Add()添加此“播放”,则将插入“播放”的每个子对象-不管是否已经存在。 Since I need to update existing entries I have to do something about that. 由于我需要更新现有条目,因此我必须对此做一些事情。 I guess the problem here is that I have no Id in the XML whatsoever. 我想这里的问题是我在XML中没有任何ID。

As far as I can tell I have the following options: 据我所知,我有以下选择:

  1. add/update the child entities in my PlayRepositories Add-Method 在我的PlayRepositories Add-Method中添加/更新子实体
  2. restructure and rewrite ParsePlayInfo() so that get all the child entities first, add or update them and then create a new Play. 重组并重写ParsePlayInfo(),以便首先获取所有子实体,添加或更新它们, 然后创建一个新的Play。 The only problem I have here is that I wanted ParsePlayInfo() to be persistence ignorant, I could work around this by 我在这里唯一的问题是我希望ParsePlayInfo()对持久性不了解,我可以通过以下方法解决此问题:
  3. creating multiple parse methods (eg ParseActors() ) and assign them to play in my controller (I'm using ASP.net MVC) after everything was parsed and added 创建了多个解析方法(例如ParseActors())并在对所有内容进行解析和添加之后将它们分配给我的控制器中播放(我正在使用ASP.net MVC)

Currently I am implementing option 1 - but it feels wrong. 目前,我正在实施选项1-但感觉不对。

What I want to do is update entities that are already in the database and insert new ones that aren't. 我想要做的是更新数据库中已有的实体,并插入没有的实体。 Do I have to call SaveChanges() before I Attach/Add Play? 我必须在附加/添加播放之前调用SaveChanges()吗? There must be a (relatively) easy solution. 必须有一个(相对)简单的解决方案。 I'd appreciate it if someone could guide me in the right direction on this one. 如果有人可以在正确的方向上指导我,我将不胜感激。

Well, since there is no answer yet I'll write one myself. 好吧,既然还没有答案,我自己写一个。

For those who are wondering, I got it to work - the code looks ugly as hell and I guess performance is even worse. 对于那些想知道的人,我可以使用它-代码看起来很难看,我想性能会更差。 But since there wont be many users and this method will only be called once a day at night anyway I am fine with it for now. 但是由于不会有太多的用户,而且无论如何我只会在晚上每天调用一次此方法,现在我可以接受。

What did I do? 我做了什么

Well, I went with option 2 and 3. 好吧,我选择了选项2和3。

private Play UpdatePlay()
{
    using (RepositoryContext context = new RepositoryContext())
    {
        HttpRequest http = new HttpRequest();
        PlayRepository rep = new PlayRepository(context);

        ActorRepository actRep = new ActorRepository(context);
    ReviewsRepository revRep = new ReviewsRepository(context);
        TheatreRepository insRep = new TheatreRepository(context);
        PartRepository partRep = new PartRepository(context);

        Parser p = new Parser();

        XDocument doc = http.GetPlayInfo();

        Theatre theatre = p.ParseTheatreInfo(doc);
        List<Actor> actors = p.ParseActorInfo(doc);
        List<PlayReviews> playReviews = p.ParseReviewsInfo(doc);

        for (int i = 0; i < actors.Count; i++)
        {
            actors[i] = actRep.AddOrUpdate(actors[i]);
        }
        for (int i = 0; i < playReviews.Count; i++)
        {
            playReviews[i].Reviews = revRep.AddOrUpdate(playReviews[i].Reviews);
        }

        theatre = insRep.AddOrUpdate(theatre);

        Play play = p.ParsePlayInfo(doc);

        List<Part> parts = GetParts(play);

        for (int i = 0; i < parts.Count; i++)
        {
            List<Actor> lec = (List<Actor>)parts[i].Actors;
            for (int j = 0; j < lec.Count; j++)
            {
                lec[j] = actRep.AddOrUpdate(lec[j]);
            }

        }

        play = rep.AddOrUpdate(play);
        context.LoadProperty(play, o => o.Theatre);
        context.LoadProperty(play, o => o.Actors);
        context.LoadProperty(play, o => o.PlayReviewss);
        context.LoadProperty(play, o => o.Parts);

        rep.Save();

        if (play.Theatre != theatre)
            play.Theatre = theatre;

        play = rep.AddParts(parts, play);

        play = rep.AddActor(actors, play);

        for (int i = 0; i < playReviews.Count; i++)
        {
            playReviews[i].Play = play;
            playReviews[i] = revRep.AddPlayInformation(playReviews[i]);
        }

        rep.Save();
        return play;
    }
}

(And on a side note, I just realized that I previously forgot to post this part of my code...) (顺便说一句,我刚刚意识到我以前忘记发布代码的这一部分...)

As you can see, Save() gets called twice - and it gets worse when you consider whats going on in AddOrUpdate(): 如您所见,Save()被调用两次-当您考虑AddOrUpdate()中发生的事情时,情况变得更糟:

public Actor AddOrUpdate(Actor entity)
{
    Actor cur = context.Actors.Where(l => l.Name == entity.Name && l.Last_Name == entity.Last_Name).FirstOrDefault();
    if (cur == null)
    {
        context.Actors.AddObject(entity);
        return entity;
    }
    else
    {
        if (!entity.Mail.IsNullOrEmptyOrWhitespace() && cur.Mail != entity.Mail)
            cur.Mail = entity.Mail;
    //there are more of these...

        return cur;
    }
}

I can't believe that this is "the right" way to do this. 我不敢相信这是做到这一点的“正确”方法。 It feels and looks just wrong. 感觉和看起来都是错误的。 Maybe EF is to blame too, take 也许英孚也要怪

FirstEntityType first = new FirstEntityType();
first.Id = 2;
List<SecondType> list = CreateList(); //Let's say this returns a List with 10 elements
context.FirstEntityType.AddObject(first);

for (int i = 0; i < list.Count; i++)
{
    list[i].First = first;
}

//just for arguments sake a second for
for (int i = 0; i < list.Count; i++)
{
    context.SecondType.AddObject(list);
}

context.SaveChanges();

I haven't tested this particular piece of code but from what I experienced is that I'll end up with 10 new entries for SecondType and if I am not wrong 11 for FirstEntityType. 我还没有测试过这段特定的代码,但是从我的经验来看,我将为SecondType最终添加10个新条目,如果我没有记错的话,那么将为FirstEntityType添加11个新条目。

Why? 为什么? Why isn't there a mechanism in EF that says "Hey, wait a minute - those are the same!" 为什么EF中没有这样一种机制:“嘿,请稍等-一样!”

Am I that wrong thinking that EF should behave like if I was using the db directly? 我是否错误地认为EF应该像直接使用db一样表现? In my example, I added "first" so I could assume that whenever I use "first" it is referenced. 在我的示例中,我添加了“ first”,因此我可以假定每当我使用“ first”时都会被引用。 (I really hope my example works as described - don't have the time nor the desire to test it) (我真的希望我的示例能按描述的那样工作-既没有时间也没有测试的欲望)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM