Using "custom data types" with entity framework - entity-framework

I'd like to know if it is possible to map some database columns to a custom data type (a custom class) instead of the basic data types like string, int, and so on. I'll try to explain it better with a concrete example:
Lets say I have a table where one column contains (text) data in a special format (e.g a number followed by a separator character and then some arbitrary string). E.g. the table looks like this:
Table "MyData":
ID |Title(NVARCHAR) |CustomData (NVARCHAR)
---+----------------+-----------------------
1 |Item1 |1:some text
2 |Item2 |333:another text
(Assume I am not allowed to change the database) In my domain model I'd like to have this table represented by two classes, e.g. something like this:
public class MyData
{
public int ID { get; set; }
public string Title { get; set; }
public CustomData { get; set; }
}
public class CustomData
{
public int ID { get; set; }
public string Text { get; set; }
public string SerializeToString()
{
// returns the string as it is stored in the DB
return string.Format("{0}:{1}", ID, Title);
}
public string DeserializeFromString(string value)
{
// sets properties from the string, e.g. "1:some text"
// ...
}
}
Does entity framework (V4) provide a way to create and use such "custom data types"?

No. Not like that, anyway.
However, you could work around this by:
Write a DB function to do the mapping and then use a defining query in SSDL.
Using one type for EF mapping and another type like you show above, and then projecting.
Add extension properties to your EF type to do this translation. You can't use these in L2E, but it may be convenient in other code.

Related

Can I change Data Annottion at Runtime based on the type of storedprocedure I am calling

Hi my Entity is ParameterDetail as follows:
public class ParameterDetail
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public string Description { get; set; }
//..other columns removed for brevity
public int LookupValueId { get; set; }
}
I call my stored procedure and load the results as follows...
List<ParameterDetail> paramDetails = this.ParameterDetails.FromSqlRaw("EXEC dbo.GE_GetStartParameter #GuidelineName={0}", guidelineName).ToList();
Now this all is working fine but now I have to call a slightly different procedure which does not have LookupValueId..
List<ParameterDetail> paramDetails =this.ParameterDetails.FromSqlRaw("EXEC dbo.GetParameterDetails #ParameterId={0}", nextParam).ToList();
I don't want to add another EntityModel just for this one column....
Can i use Mapped property (Data Annotation) at runtime somehow? Or could there be any other solution?
Can i use Mapped property (Data Annotation) at runtime somehow? Or could there be any other solution?
Not if you want EF to perform the mapping, at least not a good one. You can't change the attributes at runtime, but if you use Fluent configuration you could have two different DbContext subtypes that configure the same entity class differently. But that's not a very elegant solution.
You can always just execute the stored procedure with ADO.NET and map the data however you want.

How do you map strings in the database to enums in your model without introducing a second property?

My database has a table like this:
Cats
- CatId INT PK
- Name VARCHAR(100)
- FavoriteToy VARCHAR(100)
And my code looks like this:
Cat.cs
public int CatId { get; set; }
public string Name { get; set; }
public Toy FavoriteToy {get; set; }
StaticVariables.cs
public enum Toy { Box, Ball, StuffedAnimal }
In a normalized database I would use a lookup table in the database to store all the toys and then the Cats table would just store a ToyId. But for this situation it's a lot easier to just store the FavoriteToy as a string even though it will be redundant.
The problem is I don't know how to convert a string in the database to an enum in code without creating a second FavoriteToyString property and having FavoriteToy just be a computed that returns the enum derived from FavoriteToyString.
I've heard this might be possible in the current version of entity framework. Is that true? Can you please show me how to do this?
You may use DTO class and automapper to solve your issue :)
Generally, yes a lookup table reference is a better option since your data can comply with referential integrity. That is, No cat records with toys that your Enum hopefully doesn't contain. (Though your Enum would need to be kept in sync with the Toys table.) You can configure EF to store enumerations as a string using a bit of a trick with the mapping:
public class Cat
{
[Key]
public int CatId { get; set; }
public string Name { get; set; }
[Column("FavoriteToy")]
public string FavoriteToyMapped { get; set; }
[NotMapped]
public Toy FavoriteToy
{
get { return (Toy)Enum.Parse(typeof(Toy), FavoriteToyMapped); }
set { FavoriteToyMapped = value.ToString(); }
}
}
The caveat of this approach is that where you might use Linq to Entity to filter on your cat's favorite toy, you need to reference the FavoriteToyMapped value in the query expression because EF/DB won't know what FavoriteToy is.
I.e.
Cats with a favorite toy of "Yarn"
var catsThatLoveYarn = context.Cats.Where(c => c.FavoriteToyMapped == Toys.Yarn.ToString()).ToList();
// not
var catsThatLoveYarn = context.Cats.Where(c => c.FavoriteToy == Toys.Yarn).ToList();
// Will error because EF doesn't map that property.
Once you are working with instances of entities, that the set of entities has been pulled back from the database, you can further access/refine queries with FavoriteToy. Just be cautious and prepared for the unknown field if you use it too early and EF goes and tries to compose a query.
var threeYearOldCats = context.Cats.Where(c => c.Age == 3).ToList();
var threeYearOldCatsThatLoveYarn = threeYearOldCats.Where(c => c.FavoriteToy == Toys.Yarn).ToList();
This is Ok because the .ToList() in the first query executed the EF-to-SQL, so threeYearOldCats is now a local List<Cat> of cat entities, not an IQueryable<Cat>.

Cannot insert explicit value for identity column - into related table

I have a database first model.
My application UI provides a group of checkboxes, one for each value in Data_Type.
When the user checks one, I expect a row to be added in BUS_APPL_DATA_TYPE,
however I'm getting an error about Cannot insert explicit value for identity column in DATA_TYPE (And I absolutely do not actually want to insert data in this table)
My EF Model class for BUS_APPL has this property
public ICollection<BusApplDataType> BusApplDataType { get; set; }
And that EF Model class looks like
public partial class BusApplDataType
{
public int BusApplId { get; set; }
public int DataTypeId { get; set; }
[Newtonsoft.Json.JsonIgnore]
public BusAppl BusAppl { get; set; }
public DataType DataType { get; set; }
}
What exactly do I need to add to the BusApplDataType collection to get a record to be inserted in BUS_APPL_DATA_TYPE?
Edit:
At a breakpoint right before SaveChanges.
The item at index 2 is an existing one and causes no issues.
The item at index 3 is new. Without this everything updates fine. There is a DATA_TYPE with id 5 in the database.
The surrounding code, if it helps.
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] BusAppl item)
{
...
var existing = _context.BusAppl.FirstOrDefault(t => t.Id == id);
...
existing.BusApplDataType = item.BusApplDataType; //A bunch of lines like this, only this one causes any issue.
...
_context.BusAppl.Update(existing);
_context.SaveChanges();
return new NoContentResult();
}
My issue was that I needed to use my context to look up the actual entity, using info passed, instead of using the one with all the same values that was passed into my api directly.

Unable to cast the type 'Claim' to type 'ClaimDetail'. LINQ to Entities only supports casting EDM primitive or enumeration types

I have a problem with an EF6 and LINQ to Entities method inasmuch as I cannot cast from one class, Claim, to a derived class, ClaimDetail. I am certain that this cast would be valid but I don't know fiddle with the syntax to get the cast to work.
The relevant portions of the model look like this:
// Claim.cs
public class Claim
{
public int Id { get; set; }
// ..other properties
}
and
// ClaimDetail.cs
public class ClaimDetail : Claim
{
public string ClaimRef { get; set; }
// ..other properties
}
I have another class, Request, that looks like this:
// Request.cs
public class Request
{
public Claim Claim { get; set; }
// ..other properties
}
These classes form a part of the context, like this:
// Context.cs
public DbSet<ClaimDetail> Claims { get; set; }
public DbSet<Request> Requests { get; set; }
Now, I would like to sort a LINQ query of Requests based on a property of ClaimDetail. I have this:
sorted = intermediate.OrderBy(r => ((ClaimDetail)r.Claim).ClaimRef);
where intermediate is an IQueryable<Request>. However, on trying to materialize this query, I receive the following message:
Unable to cast the type 'Claim' to type 'ClaimDetail'. LINQ to Entities only supports casting EDM primitive or enumeration types.
How can I do this, without resorting to calling ToList() on the intermediate results? That would be far too expensive.

Entity Framework Code First Foreign Key Problems

I am trying to use EF Code First on an existing database. I first tried some of the reverse-engineering tools, but I ran into problems with that, so at the moment I am trying to hand-code some of the classes. I am having some trouble getting some of the foreign key relationships set up. Consider two tables. The first is called LocaleValueLookup:
public class LocaleValueLookup
{
public int Id { get; set; }
public Guid Guid { get; set; }
}
This table provides an Id for multi-language text held in a different table (that other table is not important for the purposes of this question). The second table is called SectionType, and it has an optional FK to LocaleValueLookup:
public class SectionType
{
public int EnumId { get; set; }
public string Name { get; set; }
public int? DefaultSectionTextLocaleValueLookupId { get; set; }
// Navigation property
public LocaleValueLookup DefaultSectionTextLocaleValueLookup { get; set; }
}
I have tried various things, including adding a [ForeignKey] attribute to the SectionType.LocaleValueLookup property, and various incantations in the DbContext.OnModelCreating() override, but when I query the DbContext, I can't get the DefaultSectionTextLocaleValueLookup to be anything but null. I can retrieve other objects from the context just fine, and I have verified that DefaultSectionTextLocaleValueLookupId is not null at least some of the time.
My OnModelBuilding() contains the following:
modelBuilder.Entity<LocaleValueLookup>()
.ToTable("LocaleValueLookup")
.HasKey(lvl => lvl.Id);
modelBuilder.Entity<LocaleValueLookup>().Property(lvl => lvl.Id).IsRequired();
modelBuilder.Entity<SectionType>()
.ToTable("SectionType")
.HasKey(st => st.EnumId);
modelBuilder.Entity<SectionType>().Property(st => st.EnumId).IsRequired();
A couple of other points:
I would prefer not to have a SectionType collection on the LocaleValueLookup object. LocaleValueLookup is a low-level class that a lot of other classes depend on, so to include a collection property on LocaleValueLookup for every other class that references it will make for an unwieldy class with a lot of collections on it that I don't need from a domain perspective.
I would prefer to do the mapping setup in DbContext.OnModelCreating() rather than using attributes on my model objects
Any help would be greatly appreciated!
It looks like your foreign key is nullable so that means an optional -> many relationship.
Could you try something like this:
modelBuilder.Entity<SectionType>()
.HasOptional(opt => opt.DefaultSectionTextLocaleValueLookup)
.WithMany() // no navigation on the other side
.HasForeignKey(fk => fk.DefaultSectionTextLocaleValueLookupId);
If you were to write a query like this you should get a value back:
var query =
from st in db.SectionTypes
where st.EnumId == 12345
select new
{
SectionType = st,
LocaleValue = st.DefaultSectionTextLocaleValueLookup
};
It will only be non-null if the foreign key has a value, obviously.