I store animals in an SQL Server database table created by Entity Framework Code First. This is the POCO for it:
[Table("Animal")]
public class Animal
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string SomeData { get; set; }
public byte[] OtherData { get; set; }
public int ExternalSourceId { get; set; }
public string ExternalSourceAnimalId { get; set; }
}
Some animals are regularly updated by an external source (there are a few sources but a specific animal is updated from maximum 1 source). Also when an external source has a new animal it needs to be inserted. Some animals are maintained locally, these have no external source.
An external source have the Animal identified by the ExternalSourceAnimalId. These are unique for one source but there are chances of 2 sources using overlapping IDs, so ExternalSourceId is also there to make the 2 of them a combined natural key.
So I get a huge list of animals from an external source and I need to insert or update them in the local database depending on the existence of the specific external key in our datebase.
This is my current solution on doing this (in a class inheriting form SharpRepository):
public void InsertOrUpdateAnimal(ExternalAnimal exAnimal, int externalSourceId)
{
var animal = DbSet.SingleOrDefault(o => o.ExternalSourceId == externalSourceId && o.ExternalSourceAnimalId == exAnimal.Id);
if (animal != null)
{
CopyData(exAnimal, animal); // copies the properties from exAnimal to animal
this.Update(animal);
}
else
{
animal = new Animal();
CopyData(exAnimal, animal);
this.Add(animal);
}
}
Since this is a bulk operation and takes quite some time I was wondering if there is a faster solution for this. For example if there is a way to upsert in a single database operation.
Related
I come from Django, and with Django's ORM when defining a model I can set options, for example:
class MyModel(Model):
CHOICES = (...) # List of choices here
# it has tu be a 2-tuple list
# but that's is not the point right now.
choice = IntegerField(choices=CHOICES) # Set the choices.
Now, when MyModel is used to create an instance, the attribute(property) choice can hold only values defined in CHOICES.
There is some like this for EntityFrameworkCore-2.0?
It is something like this possible?
class MyEntity
{
[choices=EnumDefiningChoices]
property enum choices { get; set; }
}
A property can have a single defined value, an int, string, etc in order to be stored in a database.
An approach is to use 2 tables, one for your entity and one for the choices.
MyEntity:
public class MyEntity
{
public int Id { get; set; }
// all the other properties
public int ChoiceId { get; set; } // Here is the refference for the choice
public virtual Choice Choice { get; set; }
}
Choice:
public class Choice
{
public int Id { get; set; }
public string Name { get; set; }
}
As you can see the ChoiceId will allow only values from the Choice table.
However if you want to use an Enum there is no "blocking" mechanism using EF, this must be implemented at the application level.
Indeed, there is a way!
With EF > 6 you can do something like this:
public enum Option
{
option_1 = 1,
option_2,
option_3
}
class SomeEntity
{
public Option Option { get; set; }
}
And you can get the enum option name with:
var option_name = Option.GetName(typeof(Option), some_entity.option_2);
Console.WriteLine(option_name);
References
Working with Enumerated Values in Entity Framework
Enum.GetName Method (Type, Object)
So I'm using the C# nuget wrapper around Azure Search. My problem is I have a index of products:
public class ProductDocument
{
[System.ComponentModel.DataAnnotations.Key]
public string Key { get; set; }
[IsSearchable]
public string Sku { get; set; }
[IsSearchable]
public string Name { get; set; }
[IsSearchable]
public string FullDescription { get; set; }
[IsSearchable]
public List<CustomerSkuDocument> CustomerSkus { get; set; }
}
public class CustomerSkuDocument
{
[IsSearchable]
public int AccountId { get; set; }
[IsSearchable]
public string Sku { get; set; }
}
Example data would be:
new Product() { Key= 100,Name="Nail 101",Sku = "CCCCCCCC", CustomerSkus = new List<ProductCustomerSku>()
{
new ProductCustomerSku() {AccountId = 222, CustomerSku = "BBBB"},
new ProductCustomerSku() {AccountId = 333, CustomerSku = "EEEEEEE"}
}
So the problem is around CustomerSkuDocument.
When I Search I need to pass the AccountId in as well as the search term, however the AccountId is only used for when searching the ProductCustomerSkus.
Basically an Account can have different customer skus but it's only associated to that account - I don't want a separate index per account.
So my call would be something like /AccountId=222&term=BBBB which would find the match.
However /AccountId=333&term=BBBB would not find a match.
So I'm calling it like:
SearchParameters sp = new SearchParameters();
sp.SearchMode = SearchMode.Any;
sp.QueryType = QueryType.Full;
DocumentSearchResult<ProductDocument> results =
productIndexClient.Documents.Search<ProductDocument>(term, sp);
Where term is the normal search term, tried it with adding the AccountId but it doesn't work.
Azure Search does not support repeating data structures nested under a property of the outer document. We're working on this (see https://feedback.azure.com/forums/263029-azure-search/suggestions/6670910-modelling-complex-types-in-indexes), but we still have some work to do before we can release that.
Given that, the example you're showing is not probably indexing the nested parts. Can you post the search index definition you're using? While we work in direct support for complex types, you can see your options for approach here: https://learn.microsoft.com/en-us/azure/search/search-howto-complex-data-types
From the above you'll arribe at a index structure that will also guide your query options. If all you need is equality, perhaps you can simply include the accountId and the SKU in the same field and use a collection field so you can have multiple instances. For your query you would issue a search query that requires the accountId and has the rest as optional keywords.
I'm using Entity Framework 6 Code First.
Right now my model looks the following:
public class Region
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<City> Cities { get; set; }
}
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Region Region { get; set; }
}
The thing is, I got a list of cities.
I need to lookup the region of each city (I have a local list of regions as well).
Now, I suppose I could do the following:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.Region.Id))
}
Here I will have to look up a region from the database for each city (lazy loading).
However, I only need the Id of the region, therefore it seems wasteful to me to look up the region row at every loop.
If I changed my City model to be the following:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Region")]
public int RegionId { get; set; }
[Required]
public virtual Region Region { get; set; }
}
I could do the following instead:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.RegionId)) //no region lookup at Im using the foreign id key
}
Is this correct? I mean it will save me a query for every city right?
If so, is there any reason NOT to include the foreign id keys in the model when doing Code First at all?
Is this correct? I mean it will save me a query for every city right?
It will save a join to the Regions table. You have to check it out with the SQL Profiler! For example:
List of cities with Id City1, City2 (Region not loaded City1= has RegionId 5)
Now you are looking for any City has a Region with Id 5.
// EF does not have to join the tables because you have the RegionId
if (myDbContext.Cities.Any(c => c.RegionId == 5))
{
}
If so, is there any reason NOT to include the foreign id keys in the
model when doing Code First at all?
No for me this it is a good practice! Just keep it consistent and do it for all relationships of the type 1..n/1..0or1.
I have a BlobEntity table that contains paths to files for many other tables (tableX, tableY, tableZ, etc...) in my application.
The relation between all the other tables to BlobEntity table is one to many.
Example:
tableX -> BlobTable (OTM)
tableY -> BlobTable (OTM)
tableZ -> BlobTable (OTM)
and the relation is:
public virtual ICollection<BlobEntity> BlobEntity { get; set; }
I'm not sure if this is an issue, but entity framework Code First creates a new FK column in BlobEntity table for each source table.
In my case, BlobEntity contains three FK columns for tableX, tableY and tableZ.
In order to be efficiency, i rather create one column in BlobEntity that contains the FK for the source tables.
Is it reasonable?
Please advise...
Thanks.
No, you can't do this even in plain old SQL.
You can have a foreing key pointing to more than one table; that's why you need
three columns.
If you want to do a "trick" like this, you have to manually manage the relation (I mean, no real FK), but you can't map it into EF.
What about this?
public class EntityA
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class EntityB
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class MyFiles
{
public MyFiles()
{
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityA = new List<EntityA>();
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityB = new List<EntityB>();
}
public int Id { get; set; }
public int? EntityAId {get;set;}
public int? EntityBId {get;set;}
public virtual ICollection<EntityA> FilesForEntityA { get; set; }
public virtual ICollection<EntityB> FilesForEntityB { get; set; }
}
This way you can have the FK in place and you can easily manager multiple entities.
Obviously if you have many files for each entity, you can go with a N-to-N relationship, like this.
I'd like to know how can I make Entity Framework update an object instead of always inserting a new one for each new main object.
For example:
I have these objects:
Main Object:
public class ExtraArticleAttributes
{
[Key]
public int extraarticleattributes_id { get; set; }
virtual public WorldData world_data { get; set; }
}
Its dependencie:
public class WorldData
{
[Key]
public int worlddata_id { get; set; }
public string country { get; set; }
So, how can I make Entity Framework when inserting a new ExtraArticleAttributes verify if already exists a WorldData object and only update it?
I've been reading some articles about it and I notice that Entity Framework identify an existing object in DB with a HASH code, so when I get it from an API, and try to insert It in the DB, even though the object has the same data, the Entity Framework doesn't recognize like an existed object in DB. Does exist a way of make It, without spending request to the DB to verify if the object exists, if true get It.
Set the entity state to Modified:
using System.Data.Entity;
// Assuming that there is already an existing WorldData record in the database with id 1 and country 'foo', and you want to change the country to 'bar'
using (var context = new MyContext())
{
var extraArticleAttributes = new ExtraArticleAttributes
{
world_data = new WorldData
{
worlddata_id = 1,
country = "bar"
}
};
db.ExtraArticleAttributes.Add(extraArticleAttributes);
db.Entry<WorldData>(extraArticleAttributes.world_data).State = EntityState.Modified;
db.SaveChanges();
// world data 1 country is now 'bar'
}