Entity Framework: Entity with composite key as PK/FK throws exception - entity-framework

On escalado, throws the exception. It throws with or wihtout Include.
static void Main(string[] args)
{
try
{
using (var context = new CKContext())
{
var servReprosWithIncludes = context.ServicioRepro
.Include(p => p.Categoria)
.ToList();
var escalado = context.EscaladoPrecio
//.Include(p => p.Servicio)
.ToList();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
InvalidOperationException: The value of a property that is part of an object's key does not match the corresponding property value stored in the ObjectContext. This can occur if properties that are part of the key return inconsistent or incorrect values or if DetectChanges is not called after changes are made to a property that is part of the key.
The mapping of EscaladoPrecio:
public class EscaladoPrecioMapping : EntityTypeConfiguration<EscaladoPrecio>
{
public EscaladoPrecioMapping()
{
base.HasKey(p => new { p.Desde, p.Hasta, p.ServicioReproId });
base.HasRequired(p => p.Servicio)
.WithMany()
.HasForeignKey(p => p.ServicioReproId);
base.ToTable("PreciosServicioReprografia");
}
}
The entity ServicioRepro is a part from TPT hierarchy. Looks like:
public class ServicioRepro : Producto
{
public bool IncluirPrecioClick { get; set; }
public bool IncluirPrecioPapel { get; set; }
public bool HayPapel { get; set; }
public bool HayImpresion { get; set; }
public bool PrecioPorVolumen { get; set; }
//public virtual ICollection<EscaladoPrecio> EscaladoPrecio { get; set; }
public virtual CategoriaServicioRepro Categoria { get; set; }
public virtual ServicioReproFacturacionType ServicioReproFacturacionType { get; set; }
}
On this entity you can't see the key, because the base entity Producto have it.
The entity EscaladoPrecio have 3 PK: desde, hasta and Servicio. Servicio is PK and FK.
The entity looks like (methods, overrides and members have been removed to reduce the code):
public class EscaladoPrecio : IComparable<EscaladoPrecio>, IComparable<int>, IComparable, IEntity
{
#region Declarations
private int _desde;
private int _hasta;
private double _precio;
private int _cada;
#endregion Declarations
#region Constructor
public EscaladoPrecio()
: this(1, 1, 0, 0)
{ }
public EscaladoPrecio(int desde, int hasta, double precio)
: this(desde, hasta, precio, 0)
{ }
public EscaladoPrecio(int desde, int hasta, double precio, int cada)
{
_desde = desde;
_hasta = hasta;
_precio = precio;
_cada = cada;
}
#endregion Constructor
#region Properties
public int Desde
{
get
{
return _desde;
}
set
{
_desde = value;
}
}
public int Hasta
{
get
{
return _hasta;
}
set
{
_hasta = value;
}
}
public double Precio
{
get
{
return _precio;
}
set
{
_precio = value;
}
}
public int Cada
{
get
{
return _cada;
}
set
{
_cada = value;
}
}
#endregion Properties
private int _ServicioReproId;
public int ServicioReproId
{
get
{
if (Servicio != null)
{
_ServicioReproId = Servicio.Id;
return Servicio.Id;
}
else
return 0;
}
set
{
_ServicioReproId = value;
}
}
public virtual ServicioRepro Servicio { get; set; }
}
Why throws the exception?

Why are you doing this:
public int ServicioReproId
{
get
{
if (Servicio != null)
{
_ServicioReproId = Servicio.Id;
return Servicio.Id;
}
else
return 0;
}
set
{
_ServicioReproId = value;
}
}
Your part of the key property ServicioReproId is returning 0 here potentially although it has been loaded (and stored in the context) with a value != 0 (probably). I think this part of the exception is refering to this problem: "This can occur if properties that are part of the key return inconsistent or incorrect values."
Better leave it an automatic property:
public int ServicioReproId { get; set; }

try to initialice his virtual property in the constructor of the class EscaladoPrecio()

Related

linq to entity on dbcontext.set<>

I have multiple databases, 1 common and n company db. I use code first with one migration for common and one for the companies db.
I have a base context this is inherit to 2 contexts (common, company). I try to use only the base context and remove the specified contexts, so far no problem.
My problem is following, if i try to use linq on context.Set<> then I get an InvalidOperationException "The entity type NOCompany is not part of the model for the current context".
using (NOContext db = new NOContext(connection)) {
var dbset = db.Set<NOCompany>()
.Where(company => (company.Deleted == null) || (company.Deleted == false));
foreach (var item in dbset) {
System.Diagnostics.Debug.WriteLine(item.Matchcode);
}
}
if I use this
using (NOContext db = new NOCommonContext(connection)) {
var dbset = db.Set<NOCompany>()
.Where(company => (company.Deleted == null) || (company.Deleted == false));
foreach (var item in dbset) {
System.Diagnostics.Debug.WriteLine(item.Matchcode);
}
}
then it works fine. Where is the problem?
Following excerpt from the classes
public class NOContext : DbContext, INOContext
{
public NOContext() { }
public NOContext(string connection) : base(connection) { }
#region - override DbContext -
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<EntityBase>()) {
DateTime currentDateTime = DateTime.Now;
var entity = entry.Entity;
if (entry.State == EntityState.Added) {
entity.CreateDate = currentDateTime;
entity.CreateId = NOEngine.SessionInfo.CurrentUserId;
} else if (entry.State == EntityState.Deleted) {
entry.State = EntityState.Modified;
entity.Deleted = true;
entity.DeletedDate = currentDateTime;
entity.DeletedId = NOEngine.SessionInfo.CurrentUserId;
}
entity.ModifiedDate = currentDateTime;
entity.ModifiedId = NOEngine.SessionInfo.CurrentUserId;
}
return base.SaveChanges();
}
#endregion
//database methods
}
Then I have 2 specified context
public class NOCommonContext : NOContext
{
public const string CommonCatalog = "NOCommonDb";
public NOCommonContext() { }
public NOCommonContext(string connection) : base(connection) { }
#region - DbSets -
public virtual DbSet<NOUser> Users { get; set; }
public virtual DbSet<NOCompany> Companies { get; set; }
public virtual DbSet<NOConfig> AppConfiguration { get; set; }
#endregion //DbSets
}
and
public partial class NOAppContext : NOContext
{
public NOAppContext() { }
public NOAppContext(string connection) : base(connection) { }
#region - DbSets -
public virtual DbSet<BPCard> BPCards { get; set; }
public virtual DbSet<BPContact> BPContacts { get; set; }
public virtual DbSet<HRCard> HRCards { get; set; }
#endregion //DbSets
}

EF6 - Get entity for DbUpdateCommandTree in DbCommandTreeInterceptor

I am trying to get the value of a "NotMapped" property for a Entity/class when intercepting a DbUpdateCommandTree.
I have looked through the various metadata, but I cannot find the "link" to the Entity from the CommandTree, so unfortunately I am stuck.
Is it even possible ?
public class SomeEntity
{
public int ID { get; set; }
[NotMapped]
public int SomeUnmappedProperty { get; set; }
}
public class CommandTreeInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext ctx)
{
if (ctx.OriginalResult.DataSpace == DataSpace.SSpace)
{
var updateCommand = ctx.OriginalResult as DbUpdateCommandTree;
if (updateCommand != null)
{
// I would like to get a value of a specific property here.
// Pseudo code
var val = updateCommand.Entity.GetPropertyValue("SomeUnmappedProperty") as int;
}
}
}
}

Best Practices to localize entities with EF Code first

I am developing a domain model using EF Code First to persist the data. I have to add support for multilanguage and I would like not to contaminate the domain model with location concepts.
I like that in database exists a ProductTranslate table with title and Language fields but in my domain title belongs to the Product entity.
Someone knows how to get this?
Here is what I use and works well with code first.
Define a base Translation class:
using System;
public abstract class Translation<T> where T : Translation<T>, new()
{
public Guid Id { get; set; }
public string CultureName { get; set; }
protected Translation()
{
Id = Guid.NewGuid();
}
}
Define a TranslationCollection class:
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
public class TranslationCollection<T> : Collection<T> where T : Translation<T>, new()
{
public T this[CultureInfo culture]
{
get
{
var translation = this.FirstOrDefault(x => x.CultureName == culture.Name);
if (translation == null)
{
translation = new T();
translation.CultureName = culture.Name;
Add(translation);
}
return translation;
}
set
{
var translation = this.FirstOrDefault(x => x.CultureName == culture.Name);
if (translation != null)
{
Remove(translation);
}
value.CultureName = culture.Name;
Add(value);
}
}
public T this[string culture]
{
get
{
var translation = this.FirstOrDefault(x => x.CultureName == culture);
if (translation == null)
{
translation = new T();
translation.CultureName = culture;
Add(translation);
}
return translation;
}
set
{
var translation = this.FirstOrDefault(x => x.CultureName == culture);
if (translation != null)
{
Remove(translation);
}
value.CultureName = culture;
Add(value);
}
}
public bool HasCulture(string culture)
{
return this.Any(x => x.CultureName == culture);
}
public bool HasCulture(CultureInfo culture)
{
return this.Any(x => x.CultureName == culture.Name);
}
}
You can then use those classes in your entities, e.g.:
using System;
using System.Globalization;
public class HelpTopic
{
public Guid Id { get; set; }
public string Name { get; set; }
public TranslationCollection<HelpTopicTranslation> Translations { get; set; }
public string Content
{
get { return Translations[CultureInfo.CurrentCulture].Content; }
set { Translations[CultureInfo.CurrentCulture].Content = value; }
}
public HelpTopic()
{
Id = Guid.NewGuid();
Translations = new TranslationCollection<HelpTopicTranslation>();
}
}
With HelpTopicTranslation defined as:
using System;
public class HelpTopicTranslation : Translation<HelpTopicTranslation>
{
public Guid Id { get; set; }
public Guid HelpTopicId { get; set; }
public string Content { get; set; }
public HelpTopicTranslation()
{
Id = Guid.NewGuid();
}
}
Now, for the code first specific side of things, use the following configuration:
using System.Data.Entity.ModelConfiguration;
using Model;
internal class HelpTopicConfiguration : EntityTypeConfiguration<HelpTopic>
{
public HelpTopicConfiguration()
{
Ignore(x => x.Content); // Ignore HelpTopic.Content since it's a 'computed' field.
HasMany(x => x.Translations).WithRequired().HasForeignKey(x => x.HelpTopicId);
}
}
And add it to your context configurations:
public class TestContext : DbContext
{
public DbSet<HelpTopic> HelpTopics { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new HelpTopicConfiguration());
}
}
When all of this is done, the following migration is generated:
using System.Data.Entity.Migrations;
public partial class AddHelpTopicTable : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.HelpTopics",
c => new
{
Id = c.Guid(false),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.HelpTopicTranslations",
c => new
{
Id = c.Guid(false),
HelpTopicId = c.Guid(false),
Content = c.String(),
CultureName = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.HelpTopics", t => t.HelpTopicId, true)
.Index(t => t.HelpTopicId);
}
public override void Down()
{
DropForeignKey("dbo.HelpTopicTranslations", "HelpTopicId", "dbo.HelpTopics");
DropIndex("dbo.HelpTopicTranslations", new[] { "HelpTopicId" });
DropTable("dbo.HelpTopicTranslations");
DropTable("dbo.HelpTopics");
}
}
Any comments and/or improvements are welcome...

Entity framework Generic query in Nongeneric Property

In Entity framework I have objectsets like
public partial class Building
{
public int BuildingID { get; set; }
public string BuildingName { get; set; }
}
public partial class Town
{
public int TownID { get; set; }
public string TownName { get; set; }
}
I want to create a generic query like
T.OrderBy(o=>o.Id).Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
T is generic class can be Building or Town but problem is BuildingId and TownId has different name.I don't want to change their name as Id and create interface IIdentity.
Maybe you could try something like this:
var query = (typeof(T) == typeof(Building) ?
context.Buildings.Select(b => new { Id = b.BuildingId, Name = b.BuildingName }) :
context.Towns.Select(t => new { Id = t.TownId, Name = b.TownName }))
.OrderBy(o => o.Id)...
Not tested but that's worth a test...
You can create generic method which find a field decorated with KeyAttribute, and then performs sorting by found key field. I have tested your model, works perfectly. Look at code snippet.
DbContext:
using System.Collections.Generic;
using System.Data.Entity;
namespace ConsoleApplication28.Entities
{
public class AppDbContext : DbContext
{
public AppDbContext()
{
Database.Connection.ConnectionString = #"Data Source=NOTEBOOK-PC;Initial Catalog=StackOverflowTest;Integrated Security=True";
Database.SetInitializer(new AppDbInitializer());
}
public DbSet<Town> Towns { get; set; }
public DbSet<Building> Buildings { get; set; }
}
public class AppDbInitializer : DropCreateDatabaseIfModelChanges<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
context.Buildings.AddRange(new List<Building>
{
new Building {BuildingName = "Building1"},
new Building {BuildingName = "Building2"},
});
context.Towns.AddRange(new List<Town>
{
new Town {TownName = "Town1"},
new Town {TownName = "Town2"},
});
context.SaveChanges();
base.Seed(context);
}
}
}
Building
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28.Entities
{
public class Building
{
[Key]
public int BuildingID { get; set; }
public string BuildingName { get; set; }
}
}
Town
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28.Entities
{
public class Town
{
[Key]
public int TownID { get; set; }
public string TownName { get; set; }
}
}
Program
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ConsoleApplication28.Entities;
using System.ComponentModel.DataAnnotations;
namespace ConsoleApplication28
{
class Program
{
static void Main(string[] args)
{
const int maxDispItem = 10;
const int page = 1;
var db = new AppDbContext();
var towns = db.Towns.OrderByKey().Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
var buildings = db.Buildings.OrderByKey().Skip(maxDispItem * (page - 1)).Take(maxDispItem).ToList();
}
}
public static class Extensions
{
/// <summary>
/// Sorts the elements of a sequence in ascending order according to a key specified using KeyAttribute
/// </summary>
public static IOrderedQueryable<T> OrderByKey<T>(this IQueryable<T> source, bool isAsc = true)
{
var type = typeof(T);
var keyProperty = type.GetProperties().Single(x => x.GetCustomAttributes(typeof(KeyAttribute)).Any());
return source.OrderBy(keyProperty.Name, isAsc);
}
#region COPIED FROM THERE http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property, bool isAsc)
{
return isAsc ? source.OrderBy(property) : source.OrderByDescending(property);
}
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
#endregion
}
}

Entity Framework , how to only validate specify property

I have a demo class "User" like the following:
public partial class User {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[StringLength(30)]
[Required]
public string LoginName { get; set; }
[StringLength(120)]
[Required]
[DataType(DataType.Password)]
public string Pwd { get; set; }
[StringLength(50)]
public string Phone { get; set; }
[StringLength(100)]
public string WebSite { get; set; }
...
...
}
As you can see, "LoginName" and "Pwd" are "Required".
Some time , I only want to update user's "WebSite" , So I do like this:
public void UpdateUser(User user , params string[] properties) {
this.rep.DB.Users.Attach(user);
this.rep.DB.Configuration.ValidateOnSaveEnabled = false;
var entry = this.rep.DB.Entry(user);
foreach(var prop in properties) {
var entProp = entry.Property(prop);
//var vas = entProp.GetValidationErrors();
entProp.IsModified = true;
}
this.rep.DB.SaveChanges();
this.rep.DB.Configuration.ValidateOnSaveEnabled = true;
}
Parameter "user" like this:
new User(){
ID = 1,
WebSite = "http://www.stackoverflow.com"
}
Notice , I don't specify "LoginName" and "Pwd"
This function can work fine , but I wouldn't set ValidateOnSaveEnabled to false.
Is there any way only validate "WebSite" when ValidateOnSaveEnabled is true?
Thanks.
As I know validation executed in SaveChanges always validates the whole entity. The trick to get selective validation for property is commented in your code but it is not part of the SaveChanges operation.
I get a solution.
First define PartialValidationManager:
public class PartialValidationManager {
private IDictionary<DbEntityEntry , string[]> dics = new Dictionary<DbEntityEntry , string[]>();
public void Register(DbEntityEntry entry , string[] properties) {
if(dics.ContainsKey(entry)) {
dics[entry] = properties;
} else {
dics.Add(entry , properties);
}
}
public void Remove(DbEntityEntry entry) {
dics.Remove(entry);
}
public bool IsResponsibleFor(DbEntityEntry entry) {
return dics.ContainsKey(entry);
}
public void ValidateEntity(DbEntityValidationResult result) {
var entry = result.Entry;
foreach(var prop in dics[entry]){
var errs = entry.Property(prop).GetValidationErrors();
foreach(var err in errs) {
result.ValidationErrors.Add(err);
}
}
}
}
2, Add this Manager to My DbContext:
public class XmjDB : DbContext {
public Lazy<PartialValidationManager> PartialValidation = new Lazy<PartialValidationManager>();
protected override System.Data.Entity.Validation.DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry , IDictionary<object , object> items) {
if(this.PartialValidation.Value.IsResponsibleFor(entityEntry)) {
var result = new DbEntityValidationResult(entityEntry , new List<DbValidationError>());
this.PartialValidation.Value.ValidateEntity(result);
return result;
} else
return base.ValidateEntity(entityEntry , items);
}
...
...
Update Method :
public void UpateSpecifyProperties(T t, params string[] properties) {
this.DB.Set<T>().Attach(t);
var entry = this.DB.Entry<T>(t);
this.DB.PartialValidation.Value.Register(entry , properties);
foreach(var prop in properties) {
entry.Property(prop).IsModified = true;
}
this.DB.SaveChanges();
this.DB.PartialValidation.Value.Remove(entry);
}
Ok, it work fine.