如何从LINQ to SQL中的子集合中删除记录?
我有两个表在我的数据库连接的外键:Page(PageId,其他数据)和PageTag(PageId,标签)。我已经使用LINQ为这些表生成类,其中父页为父页,标记为子集(一对多关系)。有没有什么办法在页面类中标记PageTag记录以便从数据库中删除?如何从LINQ to SQL中的子集合中删除记录?
快速Clearification:
我想要当父DataContext的调用的SubmitChanges(),而不是之前被删除的子对象。我希望TagString的行为与Page对象的任何其他属性完全相同。
我想能够像下面的代码:
Page page = mDataContext.Pages.Where(page => page.pageId = 1);
page.TagString = "new set of tags";
//Changes have not been written to the database at this point.
mDataContext.SubmitChanges();
//All changes should now be saved to the database.
这里是我的详细情况:
为了使与标签的收集工作更容易,我添加了一个属性设置即把标签集合为一个字符串Page对象:
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
PageTags.Clear();
foreach (string tag in tags) {
PageTag pageTag = new PageTag();
pageTag.Tag = tag;
PageTags.Add(pageTag);
}
}
}
基本上,这个想法是,当标记的字符串被发送到该属性,对象的当前标签被删除,新的一组中产生他们的地方。
我现在遇到的问题是,这条线:
PageTags.Clear();
实际上不会从数据库中删除旧的标签更改提交时。
环顾四周,删除东西的“正确”方法似乎是调用数据上下文类的DeleteOnSubmit方法。但是我似乎没有从Page类中访问DataContext类。
有没有人知道一种方法来标记子元素从页面类中的数据库中删除?
经过一些更多的研究,我相信我设法找到了解决方案。从集合中删除对象时删除标记由Association属性的DeleteOnNull参数控制。
当两个表格之间的关系用OnDelete Cascade标记时,此参数设置为true。
不幸的是,无法在设计器中设置此属性,也无法从* DataContext.cs文件中的分部类中设置该属性。在不启用级联删除的情况下设置它的唯一方法是手动编辑* DataContext.designer.cs文件。
在我而言,这意味着找到页关联,并添加DeleteOnNull属性:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true)]
public Page Page
{
...
}
并添加DeleteOnNull属性:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true, DeleteOnNull = true)]
public Page Page
{
...
}
注意,需要被添加到属性PageTag类的Page属性,而不是相反。
参见:
Beth Massi -- LINQ to SQL and One-To-Many Relationships
Dave Brace -- LINQ to SQL: DeleteOnNull
在Linq to SQL实体关系图中,您是否有关系链接Page和PageTags表?如果你不这样做,那就是为什么你不能从Page类中看到PageTags类。
如果PageTags数据库表中的外键被设置为Allow Nulls,即使您在SQL Server上创建了关系,Linq to SQL也不会在将表拖入设计器时创建链接。
这是OR映射可能会变得毛茸茸的地方之一。提供这个TagString属性使事情变得更方便一些,但是从长远来看,它会混淆有人使用TagString属性时真正发生的事情。通过隐藏您执行数据修改的事实,某人可以很容易地在DataContext的范围内来访问并设置TagString而不使用Page实体,这可能会导致一些难以发现的错误。
更好的解决方案是使用L2S模型设计器在Page类上添加Tags属性,并要求在DataContext的范围内直接在Tags属性上编辑PageTags。使TagString属性为只读,因此可以进行类型化(并且仍然提供一些便利),但是可以消除设置该属性的困惑和困难。这种改变澄清了意图,并明确了正在发生的事情以及Page对象的消费者所需要做的事情。
由于Tags是Page对象的一个属性,只要它连接到DataContext,对该集合的任何更改都会正确触发数据库中的删除或插入操作,以响应Remove或Add calls。
我不希望在从集合中删除PageTag时立即删除PageTag。我期待它在父DataContext调用SubmitChanges()时被删除。 PageTagString属性的更新语义不打算与对象的任何其他数据属性不同。 – AaronSieb 2009-05-23 20:28:56
亚伦,
显然,你必须循环通过你的PageTag记录,调用DeleteOnSubmit为每一个。当您调用SubmitChanges时,Linq to SQL应该创建一个聚合查询来一次删除所有记录,因此开销应该很小。
与
foreach (PageTag tag in PageTags)
myDataContext.DeleteOnSubmit(tag);
如何从我的Page类中访问DataContext? – AaronSieb 2009-05-23 21:34:58
将DataContext成员添加到您的PageTag部分类中。 部分类PageTag DataClassesDataContext myDataContext = new DataClassesDataContext(); public string TagString { ..等等。 – 2009-05-23 22:55:27
亚伦更换
PageTags.Clear();
:
一个DataContext成员添加到您的PageTag局部类。
partial class PageTag
{
DataClassesDataContext myDataContext = new DataClassesDataContext();
public string TagString {
..等等。张贴在罗伯特·哈维的要求
较大的代码示例:
DataContext.cs文件:
namespace MyProject.Library.Model
{
using Tome.Library.Parsing;
using System.Text;
partial class Page
{
//Part of Robert Harvey's proposed solution.
MyDataContext mDataContext = new TomeDataContext();
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
//Original code, fails to mark for deletion.
//PageTags.Clear();
//Robert Harvey's suggestion, thorws exception "Cannot remove an entity that has not been attached."
foreach (PageTag tag in PageTags) {
mDataContext.PageTags.DeleteOnSubmit(tag);
}
foreach (string tag in tags) {
PageTag PageTag = new PageTag();
PageTag.Tag = tag;
PageTags.Add(PageTag);
}
}
}
private bool mIsNew;
public bool IsNew {
get {
return mIsNew;
}
}
partial void OnCreated() {
mIsNew = true;
}
partial void OnLoaded() {
mIsNew = false;
}
}
}
库方法:
public void Save() {
mDataContext.SubmitChanges();
}
public Page GetPage(string pageName) {
Page page =
(from p in mDataContext.Pages
where p.FileName == pageName
select p).SingleOrDefault();
return page;
}
用法:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string pageName, FormCollection formValues) {
Page updatedPage = mRepository.GetPage(pageName);
//TagString is a Form value, and is set via UpdateModel.
UpdateModel(updatedPage, formValues.ToValueProvider());
updatedPage.FileName = pageName;
//At this point NO changes should have been written to the database.
mRepository.Save();
//All changes should NOW be saved to the database.
return RedirectToAction("Index", "Pages", new { PageName = pageName });
}
对不起,我的错。这是行不通的。
看起来你确实需要在你的仓库中做这件事,而不是在你的Page类中。在那里,您可以访问您的原始数据上下文。
有一种方法可以“附加”原始数据上下文,但是到那时候,它已经变得相当的代码味道。
该协会是在地方,我可以通过编程访问子集。问题是对集合所做的更改(特别是删除对象)不会持久保存到数据库。 – AaronSieb 2009-05-23 20:23:45
愚蠢的问题,但你提交更改? – 2009-05-23 21:05:41
是的。当我提交更改时,我得到一个重复键错误(因为新标签重叠旧标签)。 – AaronSieb 2009-05-23 21:56:46