在ASP.NET MVC中使用外键保存实体

时间:2011-02-15 02:29:54

标签: asp.net-mvc entity-framework-ctp5 modelstate

我需要帮助做一些我认为很简单的事情。我正在使用ASP.net MVC 3和CodeFirst(CTP5)

我有两个实体:公司和位置。公司可以有很多地点。课程如下(删除所有不必要的信息)

public class Company
{    
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual ICollection<Location> Locations { get; set; }
}

public class Location
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public virtual Company Company { get; set; }

}

现在在我的控制器中,我只允许在公司的上下文中创建位置,因此公司ID始终被传入(在视图中,我在只读字段中显示公司的名称,但不允许用户更改/编辑它。

    public ActionResult Create(int companyId)
    {
        Company company = _session.Single<Company>(c => c.Id == companyId);
        Location newLocation = new Location {Company = company};
        return View(newLocation);
    } 

    [HttpPost]
    public ActionResult Create(Location location)
    {
        if (ModelState.IsValid)
        {
            _session.Add<Location>(location);
            _session.CommitChanges();                
            return RedirectToAction("Index");
        } else {
            return View(location);
        }
    }

现在,每当我尝试创建新位置时,由于未提供location.Company.Name并且是公司上的必填字段,因此ModelState.IsValid始终为false。我从不试图在这里创建一家新公司,我只是想创建一个参考正确公司的位置。我不想将Name属性添加到视图中以使ModelState得到验证。如何轻松完成?我应该传递与观点不同的东西吗?还是对于观点?

2 个答案:

答案 0 :(得分:2)

正如我在评论中所解释的那样,我偶然发现了一个小小的变化,这使得这项工作,仍然认为它是一个kludge,因为我不明白为什么我需要复制孩子Id参数,但我能做的最好/最简单的发现将来很容易更新,我将是第一个承认我不知道CodeFirst中所有(或大部分)内容的人。

我改变了位置类:

public class Location  
{      
    public int Id { get; set; }        

    [Required]      
    public string Name { get; set; }        

    [Required]      
    public virtual Company Company { get; set; }    
}

要:

public class Location  
{      
    public int Id { get; set; }        

    [Required]      
    public string Name { get; set; }        

    [Required]      
    public int CompanyId { get; set; }
    public virtual Company Company { get; set; }    
}

据我所知,这根本没有改变数据库架构,但现在唯一需要的位置(验证时)是在CompanyId字段中有一个ID,公司属性可以保留为null(之前,它试图验证适当的公司,因为需要公司类)。

现在,在创建位置时,视图需要在CompanyId中传回的唯一内容。

有人看到任何缺点吗?还是更好的方式?

答案 1 :(得分:1)

您可以轻松地在form上添加公司名称的隐藏字段。它的数据将被拾取并按预期发送到您的POST操作。

如果您根本不在服务器端使用公司名称,只要公司ID正确(因为您使用它),您就可以用任何虚拟文本填充此隐藏字段。但是,如果您的ID需要正确的公司名称,则必须将实际名称填入隐藏字段而不是虚拟文本。

反正。这将为您的公司名称添加隐藏字段

<%= Html.HiddenFor(m => m.Company.Name) %>

如果您没有将公司属性设置为任何内容,那么您也可以使用它:

<%= Html.Hidden("Company.Name", "Super duper company") %>

这个值是实际名称还是虚拟名称并不重要。您的模型将在回发时生效。

定期回发或Ajax?

我想你使用的是常规回发而不是Ajax回发。如果你确实使用了Ajax,并且你正在使用jQuery并调用$.post()$.ajax()或类似的东西,你可以随时提供你喜欢的任何对象。您可以在JavaScript代码中填写公司名称的任何值。

如果你在JavaScript中有复杂的对象,并希望将它们发送到具有强类型参数的控制器操作(因此它们将被验证),你可以使用我的this little jQuery plugin,这使得任何JSON对象都能正确准备发送到Asp.net MVC操作,因此数据将自动绑定到您的强类型数据以进行验证。 JavaScript的日期也是如此。 :)

其他信息

由于您(在评论中)指出您的实体正在进行大量工作并且您可能正在添加/删除必填字段,因此更改所有相关视图可能非常繁琐。

然而,有很多方法可以让你的生活变得更容易,因为根据对象状态,可变模型和不同的验证方案:

  1. 为创建过程提供单独的视图模型,其中还包括自动转换为应用程序模型类的功能:

    public class LocationCreate
    {
        public int Id { get; set; }
    
        [Required]
        public string Name { get; set; }
    
        [Required]
        public int CompanyId { get; set; }
    
        public Location ToModelInstance()
        {
            return new Location {
                Id = this.Id,
                Name = this.Name,
                Company = new Company {
                    Id = this.CompanyId,
                    Name = "Super duper company Ltd." // you can as well omit this line
                }
            };
        }
    }
    
  2. 使用继承:

    public class CompanyBase
    {
        public int Id { get; set; }
    }
    
    public class Company : CompanyBase
    {
        [Required]
        public string Name { get; set; }
    
        public virtual ICollection<Location> Locations { get; set; }
    }
    
    public class Location
    {
        public int Id { get; set; }
    
        [Required]
        public string Name { get; set; }
    
        [Required]
        public virtual CompanyBase Company { get; set; }
    
    }
    
相关问题