Entity Framework 如何引用一行而不是创建新行

Entity Framework How to reference a row instead of create new row

我有 2 tables GenderA​​pplicationUser。 我希望 ApplicationUser 中的每个用户在 Gender table 中引用一个性别。 但现在在创建每个 applicationUser 实例后,在 Gender table 中创建了一个新的性别。请注意,我最近将性别名称设置为唯一,但它引发了异常。

这是我的代码

public sealed class Uow : IUow, IDisposable {

    public Uow(IRepositoryProvider repositoryProvider) {
        CreateDbContext();
        repositoryProvider.DbContext = DbContext;
        RepositoryProvider = repositoryProvider;

    }

    public IApplicationUserRepository ApplicationUsers => GetRepo<IApplicationUserRepository>();
    public IFileExtensionRepository FileExtensions => GetRepo<IFileExtensionRepository>();
    public IFileRecordRepository FileRecords => GetRepo<IFileRecordRepository>();
    public IFileTypeRepository FileTypes => GetRepo<IFileTypeRepository>();
    public IGenderRepository Genders => GetRepo<IGenderRepository>();
    public IMembershipClassRepository MembershipClass => GetRepo<IMembershipClassRepository>();

    public async Task<int> CommitAsync() {
        return await DbContext.SaveChangesAsync();
    }

    public void DiscardChanges() {
        var changedEntries = DbContext.ChangeTracker.Entries()
            .Where(x => x.State != EntityState.Unchanged)
            .ToList();

        foreach (var entry in changedEntries) {
            switch (entry.State) {
                case EntityState.Modified:
                case EntityState.Deleted:
                    entry.State = EntityState.Modified; //Revert changes made to deleted entity.
                    entry.State = EntityState.Unchanged;
                    break;
                case EntityState.Added:
                    entry.State = EntityState.Detached;
                    break;
                case EntityState.Detached:
                    break;
                case EntityState.Unchanged:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }

    private T GetRepo<T>() where T : class {
        return RepositoryProvider.GetRepository<T>();
    }

    private void CreateDbContext() {
        DbContext = new DatabaseContext();
        DbContext.Configuration.ProxyCreationEnabled = false;
        DbContext.Configuration.LazyLoadingEnabled = false;
        DbContext.Configuration.ValidateOnSaveEnabled = false;
    }

    private DatabaseContext DbContext {
        get; set;
    }

    private IRepositoryProvider RepositoryProvider {
        get; set;
    }
}

public class GenderRepository : EfRepository<Gender>, IGenderRepository {
    public GenderRepository(DatabaseContext dbContext) : base(dbContext) { }

    public async Task<Gender> GetByNameAsync(string genderName) {
        return await DbContext.Gender.SingleAsync(x => x.Name == genderName);
    }
}

public class Gender {
    public const string Male = "Male";
    public const string Female = "Female";
    public const string NotSpecified = "NotSpecified";

    [Key]
    public int Id { get; set; }

    [MaxLength(12), Index(IsUnique = true)]
    public string Name { get; set; }
}

public class ApplicationUser : IdentityUser {
    [MinLength(3), MaxLength(16)]
    public string DisplayName { get; set; }

    [MaxLength(128), MinLength(2), DefaultValue(null)]
    public string FirstName { get; set; }

    [MaxLength(128), MinLength(2), DefaultValue(null)]
    public string LastName { get; set; }

    public Gender Gender{ get; set; }
}

async Task Register(RegisterViewModel model) {
    // doing some stuff
    user = new ApplicationUser {
        Email = model.Email,
        DisplayName = model.DisplayName,
        Gender = await Uow.Genders.GetByNameAsync(Gender.NotSpecified),
        MembershipClass = await Uow.MembershipClass.GetByNameAsync(MembershipClass.Normal),
    };

    user.ProfilePicture = new FileRecord();

    user.ProfilePicture.Name = "Profile";
    user.ProfilePicture.FileType = await Uow.FileTypes.GetByNameAsync(FileType.ProfileImage);
    user.ProfilePicture.Extension = await Uow.FileExtensions.GetByNameAsync(FileExtension.JPG);
    user.ProfilePicture.Size = 18932;

    // doing some stuff
    await UserManager.CreateAsync(user, model.Password);
    await Uow.CommitAsync();
}

在这种情况下,您只需将 Gender 属性 分配给现有 Gender 的引用,而不是通过 new 关键字创建一个:

var maleGender = context.Genders.Single(x => x.Name == Gender.Male);
var newUser = new ApplicationUser 
{ 
    Gender = maleGender
    //wrong approach:
    //Gender = new Gender { Name = Gender.Male }
};
context.SaveChanges();

感谢 @GertArnold 的评论。

将近 2 周后,我找到了解决方案,该解决方案位于程序流程的中间位置,我正在创建 DatabaseContext[=33 的新实例=] 这就是问题所在。 通过实践我发现在这种方法中你必须有一个 DatabaseContext 的实例,其次在我的情况下我正在创建一个新的 ApplicationUser 并且我想将 Gender 引用分配给新创建的用户,这是错误的,因为我没有引用 UserManager DatabaseContext 所以系统试图创建新的性别实例 Unique 属性 并且 exception.So 我创建了 ApplicationUser 然后在我想分配的相同上下文中获取它 Gender.