Value Object persisting via Entity framework - entity-framework

I saw this sample here , here is this suggested way to make vaueobject in DDD , take a look at this
public class SubCategory : ValueObjectBase
{
public string Name { get; private set; }
public int NumberOfPosts { get; private set; }
public SubCategory()
{
}
public SubCategory(string name, int numberOfPost)
{
Name = name;
NumberOfPosts = numberOfPost;
}
}
in my code , I'm using ValueObjectBase that don't let me to have any Id , every table in Entity framework mapping should have an Id to make a table in Database , How can I map this valueObject ann kinda imutable class ???
my mapping calss is like this :
public class SubCategoryMapping : EntityTypeConfiguration<SubCategory>
{
public SubCategoryMapping()
{
ToTable("SubCategory");
// Id ????
Property(sc => sc.Name);
Property(sc => sc.NumberOfPosts);
}
}

Related

Entity framework relations

im trying to make an app and got a slight problem. my structure looks like this:
public class BaseModel
{
[Key]
private int _Id;
public int Id {
get { return _Id; }
set { _Id = value; }
}
}
public class SupplierModel : BaseModel
{
[ForeginKey("CountryCode")] // This should map to say "se" or "no" or whatever in the CountryModel table
public virtual CountryModel Country;
}
public class CountryModel : BaseModel
{
private string _CountryCode;
[Key] // This should be another key in the table to get the actual country.
public string CountryCode {
get { return _CountryCode; }
set { _CountryCode = value; }
}
private string _CountryName;
public string CountryName {
get { return _CountryName; }
set { _CountryName = value; }
}
}
Now i want SupplierModel to link to CountryModel (Works fine by the Id) but i want it to be the country code to be the relationship not the Id between the Entities.
So accessing CountryModel.Country should map to the CountryModel table and pull out the one that matches the country model.
Hope i didnt mess it up totaly for you, hard to explain when i do not fully understand Entity framework and database relations .. trying to learn =)

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;
}
}
}
}

EF Context not keeping values after adding entity

Edit Is this post lacking sufficient information to get some guidance?
I have this method to insert an entity into the database:
public void Insert(T entity)
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
}
When I inspect entity before adding it to the context, my CustomerRole field is there. Once the add has taken place, the context doesn't seem to have it. Because of this, I am receiving this error:
Entities in 'CcDataContext.Customers' participate in the
'Customer_CustomerRole' relationship. 0 related
'Customer_CustomerRole_Target' were found. 1
'Customer_CustomerRole_Target' is expected.
These images show what I mean:
Inspecting my entity
Inspecting the context
Can anyone explain this behaviour and what I can do about it?
This is the structure of my classes (cut down for brevity):
public class Customer : BaseEntity
{
public CustomerRole CustomerRole { get; set; }
}
class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
HasRequired(t => t.CustomerRole)
.WithMany(t => t.Customers);
}
}
public class CustomerRole : BaseEntity
{
private ICollection<Customer> _customers;
public ICollection<Customer> Customers
{
get { return _customers ?? (new List<Customer>()); }
set { _customers = value; }
}
}
I can confirm that customer map is being added to the configuration and my database is built in line with them.
This is the call I am making which does the insert:
public Customer InsertGuestCustomer()
{
var customer = new Customer();
CustomerRole guestRole = GetCustomerRoleByName("Guest");
if (guestRole == null)
throw new Exception("Customer Role is not defined!");
customer.UserName = "";
customer.EmailAddress = "";
customer.Password = "";
customer.IsAdmin = false;
customer.CustomerRole = guestRole;
_customerRepository.Insert(customer);
return customer;
}
I have no other data in my database, this would be the first customer record and only one CustomerRole. My Customer table has a Foreign Key pointing to my CustomerRole.Id table / column.
Mark your navigation properties as virtual and initialize the collection property in the entity constructor rather than from the property getter.
public class Customer : BaseEntity
{
public virtual CustomerRole CustomerRole { get; set; }
}
...
public class CustomerRole : BaseEntity
{
public CustomerRole()
{
Customers = new List<Customer>();
}
public virtual ICollection<Customer> Customers { get; protected set; }
}
In your Customers property, you were returning a new List in the getter when the backing field was null, but you never assigned this to your backing field.

Change name of Identity Column for all Entities

I am in the process of creating a domain model and would like to have a "BaseEntity" class with an "Id" property (and some other audit tracking stuff). The Id property is the primary key and each Entity in my Domain Model will inherit from the BaseEntity class. Pretty straightforward stuff.....
public class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime LastUpdate { get; set; }
public string LastUpdateBy { get; set; }
}
public class Location : BaseEntity
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
Using the example above, I would like to map the "Id" field to a "LocationId" column. I understand that I can use the modelBuilder to do this for each entity explicitly by doing something like this:
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
But I would like to do this for every Entity in my domain model and it would be ugly.
I tried the following bit of reflection but did not have any luck. For whatever reason, the compiler "cannot resolve symbol type":
foreach (var type in GetTypesInNamespace(Assembly.Load("Domain.Model"),"Domain.Model"))
{
modelBuilder.Entity<type>().Property(x=>x.Id).....
}
Is there a way to define a convention to override the default PrimaryKey convention to map my "Id" property to a "ClassNameId" property in the database? I am using Entity Framework 6.
You should take a look at Custom Code First Conventions. You need EF6 for it to work, but it looks like you're already using it.
Just to give you an overview, take a look at the following convention I've used to convert PascalCase names to underscore names. It includes a convention for id properties... It also includes an optional table name prefix.
public class UnderscoreNamingConvention : IConfigurationConvention<PropertyInfo, PrimitivePropertyConfiguration>,
IConfigurationConvention<Type, ModelConfiguration>
{
public UnderscoreNamingConvention()
{
IdFieldName = "Id";
}
public string TableNamePrefix { get; set; }
public string IdFieldName { get; set; }
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
var columnName = propertyInfo.Name;
if (propertyInfo.Name == IdFieldName)
columnName = propertyInfo.ReflectedType.Name + IdFieldName;
configuration().ColumnName = ToUnderscore(columnName);
}
public void Apply(Type type, Func<ModelConfiguration> configuration)
{
var entityTypeConfiguration = configuration().Entity(type);
if (entityTypeConfiguration.IsTableNameConfigured) return;
var tableName = ToUnderscore(type.Name);
if (!string.IsNullOrEmpty(TableNamePrefix))
{
tableName = string.Format("{0}_{1}", TableNamePrefix, tableName);
}
entityTypeConfiguration.ToTable(tableName);
}
public static string ToUnderscore(string value)
{
return Regex.Replace(value, "(\\B[A-Z])", "_$1").ToLowerInvariant();
}
}
You use it like this
modelBuilder.Conventions.Add(new UnderscoreNamingConvention { TableNamePrefix = "app" });
EDIT: In your case, the Apply method should be something like this:
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
if (propertyInfo.Name == "Id")
{
configuration().ColumnName = propertyInfo.ReflectedType.Name + "Id";
}
}
Try this out in your DbContext class;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<int>()
.Where(p => p.Name.Equals("Id"))
.Configure(c => c.HasColumnName(c.ClrPropertyInfo.ReflectedType.Name + "Id"));
}
int is the CLR Type of my Primary Key fields. I want to refer to all keys in code as Id but DBA's require keys to be Id with Table entity name prefix. Above gives me exactly what I want in my created database.
Entity Framework 6.x is required.
In Entity Framework 6 Code First:
modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
and update-database...
Change in model
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id { get; set; }
to:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long role_id { get; set; }
Then remove this:
//modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
A start to the Dynamic approach if NOT using custom conventions
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
You can do this using reflection on the context. Pseudo Code as explanation:
Reflect Context to get a list of POCO names
For each POCO in a dbcontext.
Map Property Id -> string PocoName+Id
Here are the extensions I use for this type of solution.
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetModelTypes(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetTypes(propList);
}
// DBSet Types POCO types as IEnumerable List
public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
.Name.StartsWith("DbSet"))
.Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
}
private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.PropertyType.GenericTypeArguments[0].Name)
.ToList();
return modelTypeNames;
}
private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.Name)
.ToList();
return modelNames;
}
However, you will still need to employee dynamic lambda to finish.
Continue that topic here: Dynamic lambda example with EF scenario
EDIT:
Add link to another question that address the common BAse Config class approach
Abstract domain model base class when using EntityTypeConfiguration<T>
Piggybacking on #Monty0018 's answer but this just need to be updated a little if, like me, you're using Entity Framework 7 and/or SQLite.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
try
{
_builder = modelBuilder;
var typeName = typeof(T).Name;
_builder
.Entity(typeof(T))
.Property<int>("Id")
.ForSqliteHasColumnName(typeName + "Id");
}
catch (Exception e)
{
throw e;
}
}

How to achieve custom object materialization from DB using Entity Framework

I am fairly new to Entity Framework and investigating converting some legacy data access code to using EF. I want to know if the following is possible in EF and if yes how.
Say I have a Customer table like this
CustomerId | ProductId | StartDate | EndDate
--------------------------------------------
100 | 999 | 01/01/2012| null
Say I also load Product data from somewhere else (like an XML file) as a cache of product objects.
public class Customer
{
public int CustomerId {get;set;}
public int Product {get;set}
public DateTime StartDate {get;set;}
public DateTime? EndDate {get;set;}
}
public class Product
{
public int ProductId {get;set;}
public int Description {get;set}
}
Currently in CustomerDal class the method uses a StoredProc to get a Customer object like this
Customer GetCustomer(int customerId)
{
// setup connection, command, parameters for SP, loop over datareader
Customer customer = new Customer();
customer.CustomerId = rdr.GetInt32(0);
int productId = rdr.GetInt32(1);
// ProductCache is a singleton object that has been initialised before
customer.Product = ProductCache.Instance.GetProduct(productId);
customer.StartDate = rdr.GetDateTime(2);
customer.EndDate = rdr.IsDbNull(3) ? (DateTime?)null : rdr.GetDateTime(3);
return customer;
}
My question is this possible using EF when it materializes the Customer object it sets the Product property not from the DB but by another method, in this case from an in memory cache. Similary when saving a new Customer object it only gets the ProductId from the Products property and saves the value in DB.
If you attach your product instances to the EF context then when loading a Customer the Product property will be automatically filled from memory without a query to database as long as the product that is associated to the customer is already attached.
For example, starting with these entities:
public class Customer
{
public int Id { get; set; }
public int ProductId { get; set; }
public string Name { get; set; }
public Product Product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Description { get; set; }
}
Products will be available globally, for simplicity, lets make it a static class:
public static class CachedProducts
{
public static Product[] All
{
get
{
return new Product[] { new Product { Id = 1, Description = "Foo" } };
}
}
}
With this in mind we just need to assure that every EF context starts with all the products attached to it:
public class CustomerContext : DbContext
{
public CustomerContext()
{
// Attach products to context
Array.ForEach(CachedProducts.All, p => this.Products.Attach(p));
}
public DbSet<Customer> Customers { get; set; }
public DbSet<Product> Products { get; set; }
}
And finally, to make the sample complete and runnable we seed the database, request a customer and print the associated product description:
public class DatabaseInitializer : CreateDatabaseIfNotExists<CustomerContext>
{
protected override void Seed(CustomerContext context)
{
var p = new Product { Id = 1, Description = "Foo" };
var c = new Customer { Id = 1, Product = p, Name = "John Doe" };
context.Customers.Add(c);
context.SaveChanges();
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<CustomerContext>(new DatabaseInitializer());
using (var context = new CustomerContext())
{
var customer = context.Customers.Single(c => c.Id == 1);
Console.WriteLine(customer.Product.Description);
}
}
}
If you attach a profiler to SQL Server you will notice that the customer is loaded from database but no query is performed to obtain the product since it is already attached to the context. This works when loading a customer and also when saving a new customer with an associated product.
Disclaimer: I'm not an EF expert so this approach may have some undesired side effects that I'm unable to consider.