Question

In an attempt to write some clean model class libraries, I am using EF5 Code First. Using EntityTypeConfiguration various properties are mapped to columns of 2 VIEWS hosted on a SQL Server 2005 server instance.

CAUTION: I know there is some bad table column naming going on, but I'll have to cope with this as it is an existing database. Please forgive me.

After doing the plumbing, I can query client accounts...

var context = new Data.EntityContext();
public IQueryable<ClientAccount> GetClients(List<string> usernameOrEmail)
{
    return context.ClientAccount.Where(p => usernameOrEmail.Contains(p.UserName) || usernameOrEmail.Contains(p.Email)).Include("Company").AsQueryable();
}

...matching clients are returned, including company details.

However, when trying to obtain a list of Companies only, the result contains loads of duplicates.

var companies = data.Companies.Where(c => c.IsActive).OrderBy(c => c.Name);

Using the SQL Server Profiler I found that, for reasons unclear to me, the engine comes up with a LEFT OUTER JOIN in order to include the client's userId as well:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Company] AS [Company], 
    [Extent1].[Address] AS [Address], 
    [Extent1].[AddressSuffix] AS [AddressSuffix], 
    [Extent1].[Zip] AS [Zip], 
    [Extent1].[City] AS [City], 
    [Extent1].[State] AS [State], 
    [Extent1].[CountryName] AS [CountryName], 
    [Extent1].[Telephone] AS [Telephone], 
    [Extent1].[Fax] AS [Fax], 
    [Extent1].[Url] AS [Url], 
    [Extent1].[Latitude] AS [Latitude], 
    [Extent1].[Longitude] AS [Longitude], 
    [Extent1].[isPublic] AS [isPublic], 
    [Extent1].[active] AS [active], 
    [Extent1].[parent] AS [parent], 
    [Extent2].[userId] AS [userId]
FROM  [dbo].[VIEW_CompaniesCountryContinent] AS [Extent1]
    LEFT OUTER JOIN [dbo].[PassportAccounts] AS [Extent2] ON ([Extent2].[companyId] IS NOT NULL) AND ([Extent1].[Id] = [Extent2].[companyId])
WHERE 1 = [Extent1].[active]

I am puzzled why, since I have no link to the Clients from the Company model. The VIEW doesn't include the user table and only targets the companies table.

It is clear that I am missing out on something, I hope you can get me back on track. Below you will find some details about the model, context and entitytypeconfigurations. Thanks!

Code info:

The Model is rather simple...

public class ClientAccount : IUserAccount
{
    public ClientAccount() { }

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

    [DisplayName("User Name")]
    public string UserName { get; set; }

    [DisplayName("First Name")]
    public string FirstName { get; set; }

    [DisplayName("Last Name")]
    public string LastName { get; set; }

    [DisplayName("Job Title")]
    public string JobTitle { get; set; }

    [DisplayName("Company")]
    public virtual Company Company { get; set; }

    [DisplayName("Direct Phone")]
    public string PhoneDirect { get; set; }

    [DisplayName("Mobile phone")]
    public string PhoneMobile { get; set; }

    [DisplayName("Registration Date")]
    public DateTime DateRegistered { get; set; }

    [DisplayName("Last Login")]
    public DateTime LastLogin { get; set; }

    [EmailAddress]
    [DisplayName("Email")]
    public string Email { get;set; }

    public bool IsApproved { get; set; }
    public bool IsActive { get; set; }
    public bool IsLockedOut { get { return !IsActive; } }
    public bool IsOnline
    {
        get { return (LastLogin.AddMinutes(10) > DateTime.Now); }
    }

    public DateTime LastActivityAt  {   get; set; }
}

public class Company
{
    [Key]
    public int ID { get; set; }

    public string Name { get; set; }

    public string Address { get; set; }
    public string Address2 { get; set; }

    public string Zip { get; set; }
    public string City { get; set; }
    public string State { get; set; }

    public string Country { get; set; }

    public string Telephone { get; set; }
    public string Fax { get; set; }
    public string Url { get; set; }

    public string TimeZone { get; set; }
    public Coordinate Coordinate { get; set; }

    public bool IsPublic { get; set; }
    public bool IsActive { get; set; }

    public int ParentCompanyId { get; set; }

}

public class Coordinate
{       
    public decimal? Latitude { get; set; }
    public decimal? Longitude { get; set; }
}

So far nothing special (right?). The DbContext:

public class EntityContext : DbContext
{

    public EntityContext() : base("Name=EntityContext")
    {
        Database.SetInitializer<EntityContext>(null);
    }

    public EntityContext(string connectionString)
        : base(connectionString)
    {
        Database.SetInitializer<EntityContext>(null);
    }

    public DbSet<ClientAccount> ClientAccount { get; set; }
    public DbSet<Company> Companies { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new CompanyConfiguration());
        modelBuilder.Configurations.Add(new ClientAccountConfiguration());

        base.OnModelCreating(modelBuilder);
    }
}

And the EntityTypeConfigurations...

public class ClientAccountConfiguration : EntityTypeConfiguration<ClientAccount>
{
    public ClientAccountConfiguration()
        : base()
    {
        HasKey(p => p.ClientId);

        ToTable("ClientAccounts"); // VIEW ClientAccounts

        Property(p => p.ClientId)
            .HasColumnName("userId")
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
            .IsRequired();

        Property(p => p.Email)
            .HasColumnName("userEmail")
            .IsRequired();

        Property(p => p.UserName)
            .HasColumnName("userLogin")
            .IsRequired();

        Property(p => p.FirstName).HasColumnName("userFirstName");
        Property(p => p.LastName).HasColumnName("userLastName");
        Property(p => p.PhoneDirect).HasColumnName("userPhoneDirect");
        Property(p => p.PhoneMobile).HasColumnName("userPhoneMobile");

        Property(p => p.JobTitle)
            .HasColumnName("userPosition");

        HasOptional(p => p.Company)
            .WithOptionalDependent()
            .Map(p => p.MapKey("companyId"));

        Property(p => p.IsApproved).HasColumnName("Employed");
        Property(p => p.IsActive).HasColumnName("active");
        Property(p => p.LastActivityAt).HasColumnName("updated");
        Property(p => p.LastLogin).HasColumnName("LastLogin").IsOptional();
        Property(p => p.DateRegistered).HasColumnName("created");

    }
}

public class CompanyConfiguration : EntityTypeConfiguration<Company>
{
    public CompanyConfiguration()
    {
        this.HasKey(c => c.ID);


        this.ToTable("VIEW_CompaniesCountryContinent");
        this.Property(c => c.ID)
            .HasColumnName("Id")
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        this.Property(c => c.Name)
            .HasColumnName("Company")
            .IsRequired();

        this.Property(c => c.Address).HasColumnName("Address");
        this.Property(c => c.Address2).HasColumnName("AddressSuffix");
        this.Property(c => c.Country).HasColumnName("CountryName");
        this.Property(c => c.City).HasColumnName("City");

        this.Property(c => c.Zip).HasColumnName("Zip");
        this.Property(c => c.State).HasColumnName("State");

        this.Property(c => c.Coordinate.Latitude).HasColumnName("Latitude");
        this.Property(c => c.Coordinate.Longitude).HasColumnName("Longitude");

        this.Property(c => c.TimeZone).HasColumnName("timezone");

        this.Property(c => c.IsPublic).HasColumnName("isPublic");
        this.Property(c => c.IsActive).HasColumnName("active");

        this.Property(c => c.ParentCompanyId).HasColumnName("parent");

    }
}
Was it helpful?

Solution

Following up on the solution provided by Aron.

After implementing this fix, I quickly ran into the problem that I was unable to update the Company of the ClientAccount object. This makes sense as the model did not allow for the CompanyId property of the ClientAccount object to be updated.

The foreign key property has be included in the model, secondly the property name has to match the key property name of the object it points to. This also solved my original issue. It did require a change to the (already existing) Company model though, which will have its effect on other projects that already use the Company class.

The changes:

In my model I added the CompanyId property and changed the name of the Company id property from ID to CompanyId (in order to match both property names):

public class ClientAccount : IUserAccount
{
    public ClientAccount() { }

    ...

    public Nullable<int> CompanyId { get; set; }

    [DisplayName("Company")]
    public virtual Company Company { get; set; }

    ...
}

public class Company
{
    [Key]
    public int CompanyId { get; set; }

    public string Name { get; set; }

    ...

}

In the EntityTypeConfiguration I mapped the new CompanyId property to the corresponding database column name, and can leave out the Company mapping.

public class ClientAccountConfiguration : EntityTypeConfiguration<ClientAccount>
{
    public ClientAccountConfiguration()
        : base()
    {
        ...

        Property(p => p.CompanyId).HasColumnName("companyId");

        /*
        HasOptional(p => p.Company)
            .WithOptionalDependent()
            .Map(p => p.MapKey("companyId"));
        */

        ...
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top