Ошибка LINQ to SQL INSERT

Я испытываю очень неприятную проблему при попытке вставить новую запись с помощью LINQ to SQL. Если я прохожу через этот код, иногда он вставляет новую запись, но в большинстве случаев это не так. Когда это не удается, я вижу следующую ошибку.

Не удается вставить значение NULL в столбец «Имя» таблицы «Производитель оборудования»; столбец не допускает пустых значений. ВСТАВИТЬ не удается. Заявление было прекращено.

Эта ошибка жалуется на то, что поле «Имя» имеет значение null, но этого не должно быть. Когда я отлаживаю и просматриваю эту коллекцию ["Имя"] имеет значение, которое я ввел в форму.

Вот оператор создания таблицы.

CREATE TABLE [EquipmentManufacturer] (
  [EquipmentManufacturerID] [int] IDENTITY(1,1) NOT NULL,
  [Name] [nvarchar](50) NOT NULL,

 CONSTRAINT [PK_EquipmentManufacturer] PRIMARY KEY CLUSTERED 
 (
    [EquipmentManufacturerID] ASC
 ) ON [PRIMARY]
) ON [PRIMARY]

Вот контроллер ASP.NET MVC и действие «Создать», где я пытаюсь добавить новую запись.

public partial class EquipmentManufacturerController : Controller
{
  private IRepository<EquipmentManufacturer> reposManu;

  // POST: /EquipmentManufacturer/Create
  [AcceptVerbs(HttpVerbs.Post)]
  public virtual ActionResult Create(FormCollection collection)
  {
    EquipmentManufacturer entity = reposManu.New();
    try
    {
      //HACK: Something screwy is going on here the entity oject doesn't always get updated correctly
      //UpdateModel(entity);

      entity.Name = collection["Name"];
      reposManu.Insert(entity);
      reposManu.SubmitChanges();

      return RedirectToAction("Details", new { id = entity.EquipmentManufacturerID });
    }
    catch (RulesException ex)
    {
      ex.AddModelStateErrors(ModelState, "EquipmentManufacturer");
      return ModelState.IsValid ? RedirectToAction("Create")
        : (ActionResult)View();
    }
  }
}

Вот представление Create.aspx.

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Create</h2>

    <%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

    <% using (Html.BeginForm()) {%>

        <fieldset>
            <legend>Fields</legend>
            <p>
                <label for="Name">Name:</label>
                <%= Html.TextBox("Name") %>
                <%= Html.ValidationMessage("Name") %>
            </p>
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>

    <% } %>
    <%= Html.ClientSideValidation<EquipmentManufacturer>() %>

    <div>
        <%=Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

Вот реализация репозитория, которую я использую.

public class Repository<T> : IRepository<T> where T : class
{
  public IDataContext DC { get; set; }

  public Repository(IDataContext dataContext)
  {
      DC = dataContext;
  }

  /// <summary>
  /// Return all instances of type T.
  /// </summary>
  /// <returns></returns>
  public IEnumerable<T> All()
  {
      return GetTable;
  }

  /// <summary>
  /// Return all instances of type T that match the expression exp.
  /// </summary>
  /// <param name="exp"></param>
  /// <returns></returns>
  public IQueryable<T> Find(Expression<Func<T, bool>> exp)
  {
      return GetTable.Where<T>(exp);
  }

  /// <summary>See IRepository</summary>
  /// <param name="exp"></param>
  /// <returns></returns>
  public T Single(Expression<Func<T, bool>> exp)
  {
      return GetTable.SingleOrDefault(exp);
  }

  /// <summary>See IRepository</summary>
  /// <param name="exp"></param>
  /// <returns></returns>
  public T First(Expression<Func<T, bool>> exp)
  {
      return GetTable.First(exp);
  }

  /// <summary>See IRepository</summary>
  /// <param name="entity"></param>
  public virtual void Delete(T entity)
  {
      DC.Context.GetTable<T>().DeleteOnSubmit(entity);
  }

  /// <summary>
  /// Create a new instance of type T.
  /// </summary>
  /// <returns></returns>
  public virtual T New()
  {
      T entity = Activator.CreateInstance<T>();
      GetTable.InsertOnSubmit(entity);
      return entity;
  }

  /// <summary>
  /// Adds an insance T.
  /// </summary>
  /// <returns></returns>
  public virtual void Insert(T entity)
  {
      GetTable.InsertOnSubmit(entity);
  }

  /// <summary>
  /// Update entity.
  /// </summary>
  /// <returns></returns>
  public virtual void Update(T entity)
  {
      DC.Context.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, entity);
  }

  /// <summary>See IRepository</summary>
  public void SubmitChanges()
  {
      DC.SubmitChanges();
  }

  private string PrimaryKeyName
  {
      get { return TableMetadata.RowType.IdentityMembers[0].Name; }
  }

  private System.Data.Linq.Table<T> GetTable
  {
      get { return DC.Context.GetTable<T>(); }
  }

  private System.Data.Linq.Mapping.MetaTable TableMetadata
  {
      get { return DC.Context.Mapping.GetTable(typeof(T)); }
  }

  private System.Data.Linq.Mapping.MetaType ClassMetadata
  {
      get { return DC.Context.Mapping.GetMetaType(typeof(T)); }
  }
}

person mhinton    schedule 25.08.2009    source источник


Ответы (4)


Это потому, что вы дважды вызываете .InsertOnSubmit(entity)?

Вы вызываете его один раз в New()

public virtual T New()
{
    T entity = Activator.CreateInstance<T>();
    GetTable.InsertOnSubmit(entity);
    return entity;
}

А потом снова в .Insert()

public virtual void Insert(T entity)
{
    GetTable.InsertOnSubmit(entity);
}

Лично я бы удалил GetTable.InsertOnSubmit(entity) из метода New().

Причина в том, что я думаю, что для пользователя репозитория было бы лучше специально вставить объект, а не автоматически устанавливать его для вставки каждый раз, когда они создают новый.

HTHs,
Чарльз

person Charlino    schedule 26.08.2009
comment
Кажется, это решает мою проблему, однако я не совсем уверен, почему. Если я закомментирую GetTable.InsertOnSubmit(entity) из метода Repository New, он сработает. Если я оставлю вызов InsertOnSubmit в методе Repository New и закомментирую вызов reposManu.Insert(entity) в контроллере, произойдет сбой. - person mhinton; 26.08.2009
comment
Судя по тому, что вы только что сказали, имеет смысл, что когда вы вызываете .InsertOnSubmit(entity), для его сохранения требуется копия объекта. Поэтому, когда вы вызываете .InsertOnSubmit для новой марки EquipmentManufacturer, она пытается вставить ее копию, которая не содержит значений, поэтому она не будет соответствовать ограничениям вашей базы данных. - person Charlino; 26.08.2009
comment
Спасибо, это действительно имеет смысл. - person mhinton; 27.08.2009

Я столкнулся с этой проблемой, потому что я установил параметр Auto Generated Value в моем файле *.dbml Ling2SQL на true; Я изменил его на false, и ошибка исчезла. Это простая ошибка, но мне потребовалась целая вечность, чтобы найти ее, так как я намеренно не установил значение true.

person Mark    schedule 26.12.2011

По моему опыту, такого рода проблемы обычно возникают из-за ошибок в конфигурации сопоставления.

Несколько советов по поиску ответа:

  • Используйте SQL Profiler для отслеживания операторов INSERT, которые могут дать вам больше подсказок.

  • Дважды проверьте сопоставление для класса EquipmentManufacturer, имя может быть сопоставлено неправильно. Включите эту информацию в свой вопрос, так как она дает лучшее представление о проблеме.

person Sam    schedule 26.08.2009
comment
Я использовал SQL Profiler и вижу, что переданный параметр Name имеет значение null. Я не знаю, что происходит, потому что в этой строке reposManu.Insert(entity) entity.Name не равно null. - person mhinton; 26.08.2009
comment
Проследите, что делает LINQ to SQL, когда он извлекает значение для вставки — очевидно, что для столбца Name есть сопоставление, как и в SQL, но проверили ли вы, что запись сопоставления (атрибуты или XML) на самом деле указывает на правильный свойство/поле в вашем классе сущностей? - person Sam; 26.08.2009

Я вижу, что вы не используете «авто-привязку» (или как вы это называете, где вы принимаете сущность, которую хотите связать, в качестве параметра метода действия), а вместо этого (А) обновляете объект и (Б) устанавливаете его " Имя" из коллекции форм вручную. Возможно, я не вижу, куда привязывается "EquipmentManufacturerID"?

Является ли ошибка NULL для этого свойства EquipmentManufacturerID?

person Funka    schedule 26.08.2009
comment
Нет его в свойстве Name. Я попытался использовать сигнатуру метода, такую ​​​​как общедоступный виртуальный ActionResult Create ([Bind()] EquipmentManufacturer), и у меня все еще есть та же проблема. - person mhinton; 26.08.2009
comment
Вы пытались зарегистрировать базовый SQL, который генерирует LTS? Если бы вы могли видеть, что он отправляет на сервер, это могло бы помочь вам в дальнейшей отладке...? - person Funka; 26.08.2009