EF Code First: определение внешних ключей - PullRequest
2 голосов
/ 08 марта 2012

Я посмотрел на некоторые другие ответы и множество статей, но эта простая часть все еще ускользает от меня. Сначала я использую код EF 4.1, но с радостью перехожу на более новую версию, если это облегчает задачу. У меня есть таблица основных фактов, вот так:

namespace Spend.Models
{
    public class ExpenseItem
    {
        [Key]
        public String UniqueID_ERLineID { get; set; }
        public String ERNum { get; set; }
        public String ItemNum { get; set; }
        public String Parent_Expense_Item { get; set; }
        public String Card_Number { get; set; }
...

и несколько таблиц, свисающих с той, которая имеет многозначные отношения с ExpenseItems:

public class ExpenseItemAccounting
{
    [Key]
    public String UniqueID_Accounting { get; set; }
    public String ERLineID { get; set; }
    public String ERNum { get; set; }
    public String ItemNum { get; set; }

Как мы видим, ERLineID во второй таблице присоединяется к UniqueID_ErLineID в первой, поэтому «соглашения», на которые я обычно полагаюсь, не работают. Поэтому мне нужно использовать виртуальную коллекцию ICollection, но я бы хотел указать эти поля в качестве ссылки. Любая помощь о том, как это сделать, приветствуется.

PS. В настоящее время я не могу переименовать поля БД.

@ ЛУКА:

Я применил упомянутые вами изменения, они имеют смысл. Однако я получаю следующую ошибку:

System.Data.Entity.ModelConfiguration.ModelValidationException occurred
  Message=One or more validation errors were detected during model generation:

    System.Data.Edm.EdmAssociationType: : Multiplicity conflicts with the referential constraint in Role 'ExpenseItemAccounting_ExpenseItem_Target' in relationship 'ExpenseItemAccounting_ExpenseItem'. Because all of the properties in the Dependent Role are non-nullable, multiplicity of the Principal Role must be '1'.
    System.Data.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'ExpenseItemAccounting_ExpenseItem_Source' in relationship 'ExpenseItemAccounting_ExpenseItem'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be �1�.

  Source=EntityFramework
  StackTrace:
       at System.Data.Entity.ModelConfiguration.Edm.EdmModelExtensions.ValidateAndSerializeCsdl(EdmModel model, XmlWriter writer)
       at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
       at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
       at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
       at System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
       at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
       at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
       at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
       at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
       at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
       at System.Linq.Queryable.SelectMany[TSource,TCollection,TResult](IQueryable`1 source, Expression`1 collectionSelector, Expression`1 resultSelector)
       at EmailClient.Prog.getData() in C:\MF\Dropbox\Dev_LN_Projects\04_QA\EmailClient\EmailClient\Prog.cs:line 172
  InnerException: 

Это произошло, когда я попробовал следующий запрос linq:

        var geee = (from e in db.ExpenseItems
                        from f in db.ExpenseItemFbtItems
                        where
                        e.Item_Transaction_Date.Value.Year == 2011 &&
                        e.Item_Transaction_Date.Value.Month == 8
                        select new { A = e.UniqueID_ERLineID, B = f.ERLineID.First() });

Я действительно ожидал, что смогу сказать e.ExpenseItemAccounting.ItemNum или что-то в этом роде - нужно ли мне что-то вводить в определение ExpenseItem, чтобы достичь этого?

Моя модель настроена следующим образом. Base.OnModelCreating появился через intellisense, и я попробовал его с / без него для того же результата:

public class SpendDB : DbContext
{
    public DbSet<ExpenseAttachment> ExpenseAttachments {get; set; }
    public DbSet<ExpenseComment> ExpenseComments {get; set; }
    public DbSet<ExpenseItemAccounting> ExpenseAccountings {get; set; }
    public DbSet<ExpenseItemFbtItem> ExpenseItemFbtItems {get; set; }
    public DbSet<ExpenseItem> ExpenseItems {get; set; }
    public DbSet<ExpenseItemViolation> ExpenseItemViolations {get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<ExpenseItemAccounting>().HasOptional(e => e.ExpenseItem).WithMany().HasForeignKey(e => e.UniqueID_Accounting);
    }
}

Возможно, мне нужно поместить виртуальную коллекцию ICollection в определение ExpenseItem? Или, может быть, наоборот - скажем, modelBuilder.Entity имеет дополнительный ExpenseItemAccounting? Это звучит более интуитивно для меня, но я (очевидно) не очень хорош в этом, поэтому возьмите это с крошкой соли!

Еще раз спасибо

Ответы [ 2 ]

2 голосов
/ 08 марта 2012

Сделайте это:

public class ExpenseItemAccounting
{
    [Key]
    public String UniqueID_Accounting { get; set; }
    public ExpenseItem ExpenseItem{get;set;}
    public String ERLineID { get; set; }
    public String ERNum { get; set; }
    public String ItemNum { get; set; }
}

Тогда в вашей модели Builder используйте

modelBuilder.Entity<ExpenseItemAccounting>()
    .HasOptional(e => e.ExpenseItem).WithMany()
    .HasForeignKey(e => e.UniqueID_Accounting );

EDIT:

Чтобы настроить свойство навигации для создания коллекции на другом конце, вы можете просто добавить его следующим образом

public class ExpenseItem
{
        [Key]
        public String UniqueID_ERLineID { get; set; }
        public String ERNum { get; set; }
        public String ItemNum { get; set; }
        public String Parent_Expense_Item { get; set; }
        public String Card_Number { get; set; }
        public ICollection<ExpenseItemAccounting> ExpenseItemAccountings{ get; set; }
}

затем подключите его, изменив конфигурацию компоновщика модели следующим образом:

 modelBuilder.Entity<ExpenseItemAccounting>()
     .HasOptional(e => e.ExpenseItem).WithMany(e=> e.ExpenseItems)
     .HasForeignKey(e => e.UniqueID_Accounting );

Это соединит его так, что у ExpenseItem будет список всех дочерних ExpensItemAccounting, вы также можете добавить единственную версию этого, если это имеет больше смысла, например:

 public class ExpenseItem
    {
            [Key]
            public String UniqueID_ERLineID { get; set; }
            public String ERNum { get; set; }
            public String ItemNum { get; set; }
            public String Card_Number { get; set; }
            public ExpenseItemAccounting Parent_Expense_Item { get; set; }
    }

и использование modelBuilder для конфигурации:

modelBuilder.Entity<ExpenseItemAccounting>()
    .HasOptional(e => e.ExpenseItem)
    .WithOptionalDependent(e=>e.Parent_Expense_Item);

Я думаю, что если вы также хотите, чтобы ссылки FK были связаны (а не только свойства навигации), вам нужно сделать это в отдельном утверждении, но это немного сложнее.

Посмотрите на страницу MSDN о свойствах навигации и о том, как использовать modelBuilder, поскольку в нем есть много хороших примеров продвинутых вещей.

http://msdn.microsoft.com/en-us/library/hh295843(v=vs.103).aspx

0 голосов
/ 01 октября 2012

Одним из решений является изменение значения ERLineID, имеющего нулевое значение, на TRUE

...