C# cannot remove object from DbContext - c#

Hi everyone I am trying to update my local sqldb without success.
I created a DbContext:
public class DbContextWeather1 : DbContext
{
public DbSet<WeatherRoot> Weathers { get; set; }
}
Where WeatherRoot is:
public class Coord
{
[JsonProperty("lon")]
public double Longitude { get; set; }
[JsonProperty("lat")]
public double Latitude { get; set; }
}
public class Sys
{
[JsonProperty("country")]
public string Country { get; set; }
}
public class Weather
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("main")]
public string Main { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("icon")]
public string Icon { get; set; }
}
public class Main
{
[JsonProperty("temp")]
public double Temperature { get; set; }
[JsonProperty("pressure")]
public double Pressure { get; set; }
[JsonProperty("humidity")]
public double Humidity { get; set; }
[JsonProperty("temp_min")]
public double MinTemperature { get; set; }
[JsonProperty("temp_max")]
public double MaxTemperature { get; set; }
}
public class Wind
{
[JsonProperty("speed")]
public double Speed { get; set; }
[JsonProperty("deg")]
public double WindDirectionDegrees { get; set; }
}
public class Clouds
{
[JsonProperty("all")]
public int CloudinessPercent { get; set; }
}
public class WeatherRoot
{
[JsonProperty("coord")]
public Coord Coordinates { get; set; }
[JsonProperty("sys")]
public Sys System { get; set; }
[JsonProperty("weather")]
public List<Weather> Weather { get; set; }
[JsonProperty("main")]
public Main MainWeather { get; set; }
[JsonProperty("wind")]
public Wind Wind { get; set; }
[JsonProperty("clouds")]
public Clouds Clouds { get; set; }
[JsonProperty("id")]
public int CityId { get; set; }
[JsonProperty("name")]
[Key]
public string Name { get; set; }
[JsonProperty("dt_txt")]
public string Date { get; set; }
[JsonIgnore]
public string DisplayDate => DateTime.Parse(Date).Hour.ToString();
[JsonIgnore]
public string DisplayTemp => $"{MainWeather?.Temperature ?? 0}°
{Weather?[0]?.Main ?? string.Empty}";
[JsonIgnore]
public string DisplayIcon => $"http://openweathermap.org/img/w/{Weather?
[0]?.Icon}.png";
[JsonIgnore]
public string Icon => Weather?[0]?.Icon;
//[JsonIgnore]
//public string DisplayDescription => $"{Weather?[0]?.Description}";
}
But when I am trying to delete a specific object:
public void SaveWeather(WeatherRoot weather)
{
using (var db = new DbContextWeather1())
{
db.Database.CreateIfNotExists();
//var tmp = db.Weathers;
if (db.Weathers.Any(W => W.Name.Equals(weather.Name)))
{
var bye = (from x in db.Weathers
where x.Name.Equals(weather.Name)
select x).FirstOrDefault();
db.Weathers.Remove(bye);
db.Entry(bye).State = System.Data.Entity.EntityState.Deleted;
}
var w = new WeatherRoot()
{
CityId = weather.CityId,
Clouds = weather.Clouds,
Coordinates = weather.Coordinates,
Date = weather.Date,
MainWeather = weather.MainWeather,
Name = weather.Name,
System = weather.System,
Weather = weather.Weather,
Wind = weather.Wind
};
if (w.Date == null)
{
w.Date = DateTime.Now.ToString();
}
db.Weathers.Add(w);
db.SaveChanges();
}
}
I get this error:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.Weathers_dbo.WeatherRoots_WeatherRoot_Name". The conflict occurred in database "WeatherApp.DataProtocol.DbContextWeather1", table "dbo.Weathers", column 'WeatherRoot_Name'.
The statement has been terminated.
I tried to google it, but only found related keys, which is not my case.
Does anyone can help me with this, I kind of helpless.
Thanks.

This happens due to a foreign-key constraint. You have to remove all the referenced child records before deleting the parent record.
Try to apply the following code after modifying it according to your business logic and let EF deal with it.
modelBuilder.Entity<Parent>()
.HasMany<Child>(c => c.Children)
.WithOptional(x => x.Parent)
.WillCascadeOnDelete(true);
If you are not sure about how the relationships are made, please observe the tables using SQL Server and examine Keys and Constraints as follows

From the MSDN page on DbSet.Remove:
"Marks the given entity as Deleted such that it will be deleted from the database when SaveChanges is called. Note that the entity must exist in the context in some other state before this method is called."
https://msdn.microsoft.com/en-us/library/system.data.entity.dbset.remove(v=vs.113).aspx
You could try adding:
db.SaveChanges();
under your call:
db.Weathers.Remove(bye);

Related

How to create a relation between existing entities in EF core?

I have a case scenario with two tables References and Products alreading containing many entries which can be dynamically related on demand.
public class Reference
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ReferenceId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<Product> ManyProducts { get; set; }
public Reference() {}
}
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Reference")]
public Guid ReferenceId { get; set; }
public virtual Reference OneReference { get; set; }
public Product() {}
}
When a user ask to link a reference to a product I simply do :
product.ReferenceId = reference.ReferenceId ;
await context.SaveChangesAsync() ;
The entry in Products table is updated correctly, but when I try to access a reference's related data, it does not retrieve any ?? After eager loading :
var reference = await context.References
.Include(r => r.ManyProducts)
.SingleAsync(r => r.ReferenceId == referenceId) ;
or explicit loading :
var reference = await context.References.FindAsync(referenceId) ;
await context.Entry(reference).Collection(s => s.ManyProducts).LoadAsync() ;
reference.ManyProducts is empty. So I have to do something like this :
var reference = await context.References.FindAsync(referenceId) ;
var products = await context.Products.Where(l => l.ReferenceId == referenceId).ToListAsync() ;
result.ManyProducts = products ;
which works fine, but I would like to understand why ?
I´m using DataAnnotation
Sample
public class spread
{
[Key]
public int spreadid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange origem")]
public virtual exchange exchange { get; set; } // One to one
[ForeignKey("spreadid")]
public virtual ICollection<spreadhelper> spreadhelper { get; set; } // One to many
}
public class spreadhelper
{
[Key]
public int spreadhelperid { get; set; }
[Required]
public int spreadid { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange")] // One to one
public virtual exchange exchange { get; set; }
[Required, Range(0, 200)]
public decimal spreadvalue { get; set; }
}
one to one - sample
public class exchange
{
[Key]
public int exchangeid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required, MaxLength(50)]
public string name { get; set; }
[MaxLength(128)]
public string token { get; set; }
}
One to many sample

How to fix Entity Framework Core "Argument types do not match" with Linq Select projection

When attempting a straight forward projection using Entity Framework Core and Linq, I am getting an "Argument types do not match" exception.
I have looked into possible causes and have narrowed it down to the Select that is causing the error (see below). There is a GitHub issue describing a similar situation with simple types and optional navigation entities, but none of the suggested solutions have worked for me. It is not a nullable type and I have tried casting or using Value on any child properties. I have also tried setting the relationship to required in the DbContext which isn't exactly ideal.
Here is the Linq query in the repository:
return await _dashboardContext.PresetDashboardConfig
.Where(config => config.DashboardTypeId == dashboardType && config.OrganisationType = organisationType)
.GroupBy(config => config.GroupId)
.Select(config => new DashboardConfigDTO
{
DashboardType = config.First().DashboardTypeId,
OrganisationId = organisationId,
WidgetGroups = config.Select(group => new WidgetGroupDTO
{
Id = group.Id,
Name = group.GroupName,
TabOrder = group.TabOrder,
// Problem Select below:
Widgets = group.Widgets.Select(widget => new WidgetConfigDTO
{
IndicatorId = widget.IndicatorId,
ScopeId = widget.ScopeId.ToString(),
ParentScopeId = widget.ParentScopeId.ToString(),
WidgetType = widget.WidgetType,
WidgetSize = widget.WidgetSize,
Order = widget.Order
})
})
})
.SingleOrDefaultAsync();
And the entities:
public class DashboardConfig
{
public int Id { get; set; }
public int DashboardTypeId { get; set; }
public int OrganisationType {get; set; }
public int GroupId { get; set; }
public string GroupName { get; set; }
public int TabOrder { get; set; }
}
public class PresetDashboardConfig : DashboardConfig
{
public ICollection<PresetWidgetConfig> Widgets { get; set; }
}
public class WidgetConfig
{
public int Id { get; set; }
public int IndicatorId { get; set; }
public long ScopeId { get; set; }
public long? ParentScopeId { get; set; }
public int WidgetType { get; set; }
public int WidgetSize { get; set; }
public int Order { get; set; }
}
public class PresetWidgetConfig : WidgetConfig
{
public int PresetDashboardConfigId { get; set; }
}
And finally, the DbContext ModelBuilder:
modelBuilder.Entity<PresetDashboardConfig>(entity =>
{
entity.Property(e => e.GroupName)
.HasMaxLength(32)
.IsUnicode(false);
entity.HasMany(e => e.Widgets)
.WithOne();
});
Below are the DTO classes as per Henk's comment:
public class DashboardConfigDTO
{
public int DashboardType { get; set; }
public int OrganisationId { get; set; }
public IEnumerable<WidgetGroupDTO> WidgetGroups { get; set; }
}
public class WidgetGroupDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int TabOrder { get; set; }
public IEnumerable<WidgetConfigDTO> Widgets { get; set; }
}
public class WidgetConfigDTO
{
public int IndicatorId { get; set; }
public string ScopeId { get; set; }
public string ParentScopeId { get; set; }
public int WidgetType { get; set; }
public int WidgetSize { get; set; }
public int Order { get; set; }
}

Owned type mapping EF Core fails when saving

I want to make TableSplitting using Owned Types. I have the following model:
public class Account
{
public GUID Id { get; set; }
public string Email { get; set; }
public StreetAddress Address { get; set; }
}
public class StreetAddress
{
public string Name { get; set; }
public string Address { get; set; }
public string Zipcode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public Location Location { get; set; }
}
public class Location
{
public double Lat { get; set; }
public double Lng { get; set; }
}
And I defined my mapping of the Account like this:
public override void Map(EntityTypeBuilder<Account> map)
{
// Keys
map.HasKey(x => x.Id);
// Indexs
map.HasIndex(x => x.Email).IsUnique();
// Property mappings.
map.Property(x => x.Email).HasMaxLength(255).IsRequired();
// Owned types.
map.OwnsOne(x => x.Address, cb => cb.OwnsOne(a => a.Location));
}
When I run the migration things are working and the columns are created in the database. But when I try to insert and save an address like so:
var account1 = new Account("e#mail.com", "First", "Last")
{
Address = new StreetAddress()
{
Address1 = "Street 1",
City = "City",
Zipcode = "2000",
Country = "Denmark",
Location = new Location()
{
Lat = 0.0,
Lng = 5.5
}
}
};
this.Context.Accounts.Add(account1);
I get this error
Message "The entity of 'Account' is sharing the table 'Accounts' with
'Account.Address#StreetAddress', but there is no entity of this type
with the same key value 'Id:b7662057-44c2-4f3f-2cf0-08d504db1849' that
has been marked as 'Added'."
Consider using virtual properties. This can also be used for lazy loading in your application
public class Account
{
public GUID Id { get; set; }
public string Email { get; set; }
public virtual StreetAddress Address { get; set; }
}
public class StreetAddress
{
public string Name { get; set; }
public string Address { get; set; }
public string Zipcode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public virtual Location Location { get; set; }
}
public class Location
{
public double Lat { get; set; }
public double Lng { get; set; }
}
You must add constructors and initialize owned entities.
public class Account
{
public Account (){
Address = new StreetAddress();
}
public GUID Id { get; set; }
public string Email { get; set; }
public StreetAddress Address { get; set; }
}
public class StreetAddress
{
public StreetAddress(){
Location = new Location();
}
public string Name { get; set; }
public string Address { get; set; }
public string Zipcode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public Location Location { get; set; }
}
public class Location
{
public double Lat { get; set; }
public double Lng { get; set; }
}
In other words there can't be optional owned entities.
Note: if you have a non-empty constructor you also must add empty constructor due to ef core limitations.
public class Account {
public GUID Id { get; set; }
public string Email { get; set; }
public StreetAddress Address { get; set; }
}
public class StreetAddress {
public string Name { get; set; }
public string Address { get; set; }
public string Zipcode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public Location[] Location { get; set; }
public class Location {
public double Lat { get; set; }
public double Lng { get; set; }
}
try this because once you call your object in the shape of all is in relation with Account class but the reality is you don't define that relation which I made
For an Account to be added in the database. It's references should be saved first in the table. For adding data in database it should be in the following order: Location > StreetAddress > Account
I am sure if you try adding every object to the context then build the parent it would work
meaning add the location first and then create address and reference the location object u made
add the address to the context
afterwards create and account and reference the added address in it!
Or you could add ctors to ur models that init new instances of each navigation property
one last thing, this really shouldn't be an issue as of EF Core 5 since it has tons of new bells and whistles when it comes to navigation properties
public override void Map(EntityTypeBuilder<Account> map)
{
// Keys
map.HasKey(x => x.Id);
// Indexs
map.HasIndex(x => x.Email).IsUnique();
// Property mappings.
map.Property(x => x.Email).HasMaxLength(255).IsRequired();
// Owned types.
map.OwnsOne(x => x.Address, cb => cb.OwnsOne(a => a.Location));
}
if you're using it, you really dont need the following part
just add your DbSet<T>s appropriately and thats it

Automapper nested mapping 3 levels

i have the following problem, i need access to items of sales line from salesheader, when i try access by entity works fine by lazy loading, but i try map with Automapper 6
can´t access to Item from sales header
thanks
public class SalesHeader
{
public int DocumentNo { get; set; }
public virtual ICollection<PostedSalesLine> SalesLines { get; set; }
}
public class SalesLine
{
public int LineNo { get; set; }
public int DocumentNo { get; set; }
public int ItemId { get; set; }
public virtual Item Item { get; set; }
public int Quantity { get; set; }
public decimal Amount { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public decimal UnitCost { get; set; }
public decimal UnitPrice { get; set; }
}
var result = unitOfWork.SalesHeader.GetById(documenNo);
Mapper.Initialize(cfg => cfg.CreateMap<SalesHeader, SalesHeaderDTO>()
return Mapper.Map<SalesHeaderDTO>(result);
Done!
Dont use lazy loading, it creates a mess of proxys
public IEnumerable<SalesHeader> GetAllFullDocuments()
{
return SalesContext.SalesHeader.Include(sh => sh.SalesLines.Select(i => i.Item))
.Include(sh => sh.SellToCustomer)
.Include(sh => sh.BillToCustomer)
.ToList();
}

Set property from another table with no relationship Entity Framework

I'm trying to set a property in my entity based from another entity that has no relationship one to the other. The entity sets the properties from the database and I want one of the properties to be set from another table in database, but I can't get to the bottom of it.
For example,
public class First {
[Key]
public int ProdId { get; set; }
public string Supplier { get; set; }
public virtual ICollection<Log> Logs { get; set; }
[NotMapped]
public bool IsSavedForLater
{
get
{
return Logs.Where(l =>
{
var content = l.LogContent.JsonStringToObject<History>();
return (content.ProdId == ProdId && l.TableName == "Condition");
}).Any();
}
}
}
As you can see the property IsSavedForLater is [NotMapped] and I want this property to get set from the Logs,
Here is the log Entity,
public class Log
{
[Key]
public int LogId { get; set; }
public string LogContent { get; set; }
public string TableName { get; set; }
public DateTime LogDate { get; set; }
public string BlameName { get; set; }
public bool? Deleted { get; set; }
}
is it possible to navigate like this without any database relationship?
Guys I found my solution after a huge amount of time figuring it out.
What I did is, I added first another property in my First class like this
public class First {
[Key]
public int ProdId { get; set; }
public string Supplier { get; set; }
public string TableName { get; set; }
public virtual ICollection<Log> Logs { get; set; }
}
As you can see the First class has a new property TableName that is also in my Logs class like this,
public class Log
{
[Key]
public int LogId { get; set; }
public string LogContent { get; set; }
public string TableName { get; set; }
public DateTime LogDate { get; set; }
public string BlameName { get; set; }
public bool? Deleted { get; set; }
}
Then I added in the db context class a model builder like this,
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<SmuForecasting>().HasKey(x => new { x.TableName }).HasMany(x => x.PsLogs).WithOptional().HasForeignKey(x => new { x.TableName });
}
That worked perfectly good for me but with side effects, it replaces the original key that was set to it in the beginning, so I ended up doing a total different approach.

Categories