using entity framework base model and calling fields on parent class - c#

I'm trying to use a base class with my entity framework models...
I have the following baseclass:
public class BaseModel
{
[Key]
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
public DateTime? ExpiryDate { get; set; }
public bool IsActive { get; set; }
public Guid CreatedBy { get; set; }
public Guid UpdatedBy { get; set; }
}
I then have a class that inherits from it:
public class Family : BaseModel
Basically i then want to be able to set these base fields using something like:
private void SetBaseData(ref BaseModel baseModel, Guid currentUserId)
{
if (baseModel.Id < 1)
{
baseModel.CreatedDate = _datetime.Now();
baseModel.CreatedBy = currentUserId;
baseModel.IsActive = true;
}
baseModel.UpdatedDate = _datetime.Now();
baseModel.UpdatedBy = currentUserId;
}
And then called like:
Models.Family efFamily = _mapper.Map(family);
SetBaseData(ref efFamily, family.CurrentUserId);
I'm getting this but I thought I;d be able to do this or am I completely going down the wrong route?
Error 27 Argument 1: cannot convert from 'ref FamilyOrganiser.Repository.EntityFramework.Models.Family' to 'ref FamilyOrganiser.Repository.EntityFramework.Models.BaseModel'

You could add SetBaseData method to your BaseModel class, then it would look like this:
public class BaseModel
{
// your code, properties, etc.
...
public void SetBaseData(Guid currentUserId)
{
if (this.Id < 1)
{
this.CreatedDate = _datetime.Now();
this.CreatedBy = currentUserId;
this.IsActive = true;
}
this.UpdatedDate = _datetime.Now();
this.UpdatedBy = currentUserId;
}
}
Then you can use it like this on all classes that inherit your BaseModel:
Models.Family efFamily = _mapper.Map(family);
efFamily.SetBaseData(family.CurrentUserId);

One possibility is to over ride the SaveChanges() function by creating a base DataContext class.
Doing it this way, you will never have to call any function after mapping, entity framework will do it for you and will only update the updateddt field if it exists in the table.
Here is what we did:
Create an interface IDataContext like this:
public interface IMyDataContext
{
DbConnection Connection { get; }
IDbSet<MyClass> MyClasses{ get; }
int SaveChanges();
}
and then create a partial class for the DataContext
public partial class MyDataContext : DbContext, IMyDataContext
{
static HealthDataContext()
{
Database.SetInitializer<HealthDataContext>(null);
}
public IDbSet<MyClass> MyClasses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MyClassMap());
}
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State == EntityState.Deleted || c.State == EntityState.Added || c.State == EntityState.Modified))
{
switch (entry.State)
{
case EntityState.Added:
if (entry.Entity.GetType().GetProperty("createddt") != null)
{
entry.Entity.GetType().GetProperty("createddt").SetValue(entry.Entity, new Health.Core.Helpers.RealClock().UtcNow);
}
break;
case EntityState.Deleted:
break;
case EntityState.Detached:
break;
case EntityState.Modified:
if (entry.Entity.GetType().GetProperty("updateddt") != null)
{
entry.Entity.GetType().GetProperty("updateddt").SetValue(entry.Entity, new Health.Core.Helpers.RealClock().UtcNow);
}
break;
case EntityState.Unchanged:
break;
default:
break;
}
}
}
return base.SaveChanges();
}
}
We are using Code First so I'm not sure if this will work in all scenarios.

You can do it but you need to pass in a BaseModel as the parameter has the ref modifier. If you don't, the compiler would have to box your variable, then ref is back to you, and you'd lose the value. Instead do this:
Family efFamily = new Family();
BaseModel m = (BaseModel)efFamily;
SetBaseData(ref m, new Guid());

Related

Handling User tries to add a new user with a duplicate id error in c#

I'm trying to Create user defined function that handling User tries to add a new user with a duplicate id
and I create this :
public class DuplicateIdException:Exception
{
public DuplicateIdException(String message) : base(message)
{
}
public override string Message => $" --- {base.Message} --- ";
}
public class TestDuplicateIdException
{
static void validate(List<Object> Users)
{
bool flag = false;
Type DataType = Users[0].GetType();
List<DataType> UsersConverter = Users.Cast<DataType>().ToList();
flag = UsersConverter.Any(x => x.Id == Id);
if (flag)
{
throw new DuplicateIdException("Sorry, You duplicate the Id");
}
}
}
I have many objects types and all of them have Id attribute in them, but when i call object.Id it gave an error and not working.. So how can i check them and complete the Exception ?
You can create a base class for all classes that have id ,it can be an interface or base class if you need to.
public class EntityBase
{
public int Id { get; set; }
}
then inherit/implement it
public class Users : EntityBase
{
}
public class Order : EntityBase
{
}
then validate with the base class/interface
public class ValidateDuplidateId
{
static void Validate(IEnumerable<EntityBase> entities,int id)
{
if (entities.Any(x => x.Id == id))
throw new Exception("Duplicate Id Found");
}
}
i don't like this approach of throwing exceptions for handling business logic errors, a better approach for me will be something like this
public class Validator
{
public static bool IsDuplicateId(IEnumerable<EntityBase> entities,int id)
{
if (entities.Any(x => x.Id == id))
return true;
return false;
}
}
then i will add a generic operation result class to handle any type of objects
public class OperationReuslt<T>
{
public T Result { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
}
in my user service ( for example )
public class UserSerivce
{
public OperationReuslt<Users>AddUser(int id)
{
//replace this with data from your actual data source
List<Users> users = new List<Users>();
if(Validator.IsDuplicateId(users,id))
{
return new OperationReuslt<Users>
{
Success = false,
Message = "Duplicate UserId"
};
}
// else proceed
}
}
you can use this approach as it more readable and doesn't have a performance drawback as the throwing exception approach , but in the end it all depends on your case

pass property to be used in lambda expression

I am having trouble with generics. Using examples from here, I want to pass a property for use in EF Core. I have a feeling I am overcomplicating things. Note that I would like to stick with lamba expressions instead of passing something like nameof (which I cannot use in 4.0).
class Foo
{
public string A { get; set; } // some unique string in DbSet for entity Person
public string B { get; set; } // some unique string in DbSet for entity Company
public int GetId<TEntity>(string match, Expression<Func<TEntity, string>> fieldToExamine, Expression<Func<TEntity, int>> fieldWithId, System.Data.Entity.DbContext context)
where TEntity : class
{
int retval = -1;
if (!string.IsNullOrEmpty(match))
{
var expr = (MemberExpression)fieldToExamine.Body;
var prop = (PropertyInfo)expr.Member;
var expr2 = (MemberExpression)fieldWithId.Body;
var idField = (PropertyInfo)expr2.Member;
// this works because everything is explicit
var entity = context.Set<Person>().SingleOrDefault(p => p.PersonName.Equals(match));
// ... but I want that to be generic
// how to reference the property to be evaluated?
//var entity = context.Set<TEntity>().SingleOrDefault(p => ...); // <- HELP?!
if (entity != null)
{
retval = (int)entity.GetType().GetProperty(idField.Name).GetValue(entity, null);
}
}
return retval;
}
}
static void Main(string[] args)
{
// .. omitted database stuff ..
// (for Person) what I want is to match the Name field (here: p.Name) of the Person entity to the Foo property value (here: f.A) and return the database id stored in p.PersonId
int x = f.GetId<Person>(f.A, p => p.PersonName, p => p.PersonId, context);
// same for Company, but different values and fields
int y = f.GetId<Company>(f.B, p => p.CompanyName, p => p.CompanyId, context);
}
Person class and Company class will need to implement either abstract class or interface.
public abstract class BaseEntity
{
public abstract int Id { get; set; }
public abstract string Name { get; set; }
}
public class Person : BaseEntity
{
public int PersonId { get; set; }
public override string Name { get; set; }
public override int Id
{
get { return PersonId; }
set { PersonId = value; }
}
}
public class Company : BaseEntity
{
public int CompanyId { get; set; }
public override string Name { get; set; }
public override int Id
{
get { return CompanyId; }
set { CompanyId = value; }
}
}
Then you can pass p.Name and p.CompanyId.
var entity = context.Set<TEntity>().SingleOrDefault(p => p.Name.Equals(match));

ef core .Include().Contains() null reference exception

I have my models setup like this...
public class Model1 : IEquatable<Model1>
{
public int Model1Id { get; set; }
public string Name1 { get; set; }
public Model2 Model2 { get; set; }
public int Model2Id { get; set; }
public bool Equals(Model1 other)
{
return this.Model2.Equals(other.Model2)
&& this.Name1 == other.Name1;
}
}
public class Model2 : IEquatable<Model2>
{
public int Model2Id { get; set; }
public string Name2 { get; set; }
public bool Equals(Model2 other)
{
return this.Name2 == other.Name2;
}
}
public class ModelContext : DbContext
{
public DbSet<Model1> Model1 { get; set; }
public DbSet<Model2> Model2 { get; set; }
public ModelContext(DbContextOptions<ModelContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Model1>(b =>
{
b.HasOne(m1 => m1.Model2).WithMany().HasForeignKey(m1 => m1.Model2Id);
});
}
}
then I get a null reference exception when I do this...
static void Main(string[] args)
{
var myModel1 = new Model1
{
Name1 = "myName1",
Model2 = new Model2
{
Name2 = "myName2"
}
};
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
try
{
var options = new DbContextOptionsBuilder<ModelContext>()
.UseSqlite(connection)
.Options;
//create database
using(var ctx = new ModelContext(options))
{
ctx.Database.EnsureCreated();
}
//add model objects
using (var ctx = new ModelContext(options))
{
ctx.Database.EnsureCreated();
ctx.Model1.Add(myModel1);
ctx.SaveChanges();
}
//check if exists
using(var ctx = new ModelContext(options))
{
//exception here
bool isExists = ctx.Model1.Include(m1 => m1.Model2).Contains(myModel1);
Console.WriteLine(isExists);
}
}
finally
{
connection.Close();
}
Console.ReadKey();
}
I'm expeting the Model2 instance of my m1 to be populated when I call the Include but it is still null.
but If I add AsEnumerable() to my query like..
ctx.Model1.Include(m1 => m1.Model2).AsEnumerable().Contains(model1);
then everything works fine.
EDIT:
my question is... why do I need to call AsEnumerable()? I was expecting it to work without calling AsEnumerable()..
The difference is one is an entityframe work call the other is linq to objects
Entity Framework Does not understand contains for a CLR Object
public void AddIfNotExists(Model1 model1)
{
//No Need for the include this is executed in sql, assuming the model 2
//property has already been included in your model1 this should work fine
if(false == _context.Model1.Any(x => x.Name1 == model1.Name1
&& x.Model2.Name2 == model1.Model2.Name2))
{
_context.Model1.Add(model1);
}
}
I made this based off of your logic, but chances are you really just want to check if model1.id is the the model1 set. But I have no Idea what your architecture is doing so this is what you probably want

EF Code First Configuration of shared Base Class --> Single Configuration File

I am using EF 5.0 Code First in a C# project. I have a base clase which the majority of my domain models derive from.
public abstract class AuditableModelBase
{
public Int32 CreatedByUserId { get; set; }
public DateTime CreatedDate { get; set; }
public virtual UserProfile CreatedByUserProfile { get; set; }
public Int32 UpdatedByUserId { get; set; }
public DateTime UpdatedDate { get; set; }
public virtual UserProfile UpdatedByUserProfile { get; set; }
public AuditableModelBase()
{
CreatedByUserId = 1;
CreatedDate = DateTime.UtcNow;
UpdatedByUserId = 1;
UpdatedDate = DateTime.UtcNow;
}
}
However, for every single entity I have to define the specific configurations to wire these relationships together.
// Relationships
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
I'm looking for a way to just declare one Configuration similar to the one directly above for the abstract base class instead of having to create an individual configuration file for each of my entities. I'd love to just have one file named "AuditableModelBaseMap.cs" which will have my configuration instead of "Entity1Map.cs", "Entity2Map.cs", "Entity3Map.cs", etc. especially since all of those files have the exact same code inside.
Any advice?
Thanks.
Try it like below but I didnt test it .However if I were you, I wouldnt design Audit tables this way
class AuditableModelBaseMap : EntityTypeConfiguration<AuditableModelBase>
{
public AuditableModelBaseMap ()
{
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
}
}
THIS IS MY WAY OF Doing AUDITING
public interface IEntity
{
int Id { get; set; }
}
public interface IAuditable : IEntity
{
string UpdatedBy { get; set; }
string CreatedBy { get; set; }
DateTime CreatedDate { get; set; }
DateTime UpdateDate { get; set; }
}
Now any entity which is auditable will implement this class your context will look the following
public class MYContext : DbContext, ILicensingContext
{
private readonly IAuditLogBuilder _auditLogBuilder;
public LicensingContext()
: this(new AuditLogBuilder())
{
}
private IDbSet<Device> Devices { get; set; }
private IDbSet<AuditLog> AuditLogs { get; set; }
public MyContext(IAuditLogBuilder auditLogBuilder)
{
_auditLogBuilder = auditLogBuilder;
}
/// <summary>
/// 1. Constructs the AuditLog objects from the context
/// 2. Calls SaveChanges to save the actual object modified
/// 3. It updates the Log objects constructed in step 1 to populate the IDs returned from the Db
/// 4. Saves the AuditLogs
/// </summary>
/// <returns></returns>
public override int SaveChanges()
{
var entries = ChangeTracker.Entries<IAuditable>().ToList();
_auditLogBuilder.UpdateAuditables(entries);
IEnumerable<AuditLog> auditLogEntities = _auditLogBuilder.ConstructAuditLogs(entries).ToList();
int countOfAffectedRecords = base.SaveChanges();
_auditLogBuilder.UpdateAuditLogs(auditLogEntities);
foreach (AuditLog auditLogEntity in auditLogEntities)
{
GetDbSet<AuditLog>().Add(auditLogEntity);
}
base.SaveChanges();
return countOfAffectedRecords;
}
public IDbSet<TEntity> GetDbSet<TEntity>() where TEntity : class
{
return Set<TEntity>();
}
}
public class AuditLogBuilder : IAuditLogBuilder
{
private string _username;
private string Username
{
get
{
if (HttpContext.Current != null && HttpContext.Current.User != null)
{
_username = HttpContext.Current.User.Identity.Name;
}
if (String.IsNullOrWhiteSpace(_username))
{
_username = "Service Consumer";
}
return _username;
}
}
public IEnumerable<AuditLog> ConstructAuditLogs(IEnumerable<DbEntityEntry<IAuditable>> auditableEntities)
{
var audits = new List<AuditLog>();
if (auditableEntities != null)
{
audits.AddRange(auditableEntities
.Where(
e =>
e.State == EntityState.Modified || e.State == EntityState.Added ||
e.State == EntityState.Deleted)
.SelectMany(GetAuditLogs));
}
return audits;
}
public void UpdateAuditLogs(IEnumerable<AuditLog> auditLogEntities)
{
foreach (AuditLog auditLog in auditLogEntities)
{
auditLog.RecordId = auditLog.Entity.Id;
auditLog.UpdatedBy = auditLog.Entity.UpdatedBy;
if (String.Equals(auditLog.PropertyName, "id", StringComparison.CurrentCultureIgnoreCase))
{
auditLog.NewValue = auditLog.Entity.Id.ToString(CultureInfo.CurrentCulture);
}
}
}
public void UpdateAuditables(IEnumerable<DbEntityEntry<IAuditable>> entries)
{
if (entries != null)
{
foreach (var entry in entries)
{
entry.Entity.UpdateDate = DateTime.UtcNow;
entry.Entity.UpdatedBy = Username;
if (entry.Entity.Id == 0)
{
entry.Entity.CreatedDate = DateTime.UtcNow;
entry.Entity.CreatedBy = Username;
}
}
}
}
private static IEnumerable<AuditLog> GetAuditLogs(DbEntityEntry<IAuditable> entry)
{
var audits = new List<AuditLog>();
string entityName = ObjectContext.GetObjectType(entry.Entity.GetType()).Name;
switch (entry.State)
{
case EntityState.Added:
audits.AddRange(entry.CurrentValues.PropertyNames.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue =
entry.CurrentValues[
propertyName] != null
? entry.CurrentValues[
propertyName].ToString()
: String.Empty,
PreviousValue = String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Create.ToString()
}));
break;
case EntityState.Deleted:
audits.AddRange(entry.OriginalValues.PropertyNames.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue = String.Empty,
PreviousValue =
entry.OriginalValues[
propertyName] != null
? entry.OriginalValues[
propertyName].ToString
()
: String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Delete.ToString()
}));
break;
case EntityState.Modified:
audits.AddRange(entry.OriginalValues.PropertyNames.
Where(
propertyName =>
!Equals(entry.OriginalValues[propertyName],
entry.CurrentValues[propertyName]))
.Select(propertyName =>
new AuditLog
{
EntityName = entityName,
CreateDate = DateTime.UtcNow,
NewValue =
entry.CurrentValues[propertyName] != null
? entry.CurrentValues[propertyName].ToString()
: String.Empty,
PreviousValue =
entry.OriginalValues[propertyName] != null
? entry.OriginalValues[propertyName].ToString()
: String.Empty,
PropertyName = propertyName,
Entity = entry.Entity,
Action = Actions.Update.ToString()
}));
break;
}
return audits;
}
}
Have you tried this?
public class AuditableModelBaseMap : EntityTypeConfiguration<AuditableModelBase>
{
public AuditableModelBaseMap()
{
this.HasRequired(amb => amb.CreatedByUserProfile).WithMany().HasForeignKey(amb => amb.CreatedByUserId).WillCascadeOnDelete(false);
this.HasRequired(amb => amb.UpdatedByUserProfile).WithMany().HasForeignKey(amb => amb.UpdatedByUserId).WillCascadeOnDelete(false);
}
}

Entity Framework Code First: NULL values inserted in database

I am using Entity Framework Code First Approach. I have following code to insert data into PaymentComponent and Payment tables. The data getting inserted into PaymentComponent table is not proper. It has NULL values in two columns (for one record) even though the corresponding properties in the domain objects are not null. What need to be changed in order to make it working?
EDIT
When I added the following in NerdDinners class, I am getting following result - it has new unwanted columns
public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
ORIGINAL CODE
static void Main(string[] args)
{
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
GiftCouponPayment giftCouponPayment = new GiftCouponPayment();
giftCouponPayment.MyValue=250;
giftCouponPayment.MyType = "GiftCouponPayment";
ClubCardPayment clubCardPayment = new ClubCardPayment();
clubCardPayment.MyValue = 5000;
clubCardPayment.MyType = "ClubCardPayment";
List<PaymentComponent> comps = new List<PaymentComponent>();
comps.Add(giftCouponPayment);
comps.Add(clubCardPayment);
var payment = new Payment { PaymentComponents = comps, PayedTime=DateTime.Now };
db.Payments.Add(payment);
int recordsAffected = db.SaveChanges();
}
}
DOMAIN CODE
public abstract class PaymentComponent
{
public int PaymentComponentID { get; set; }
public abstract int MyValue { get; set; }
public abstract string MyType { get; set; }
public abstract int GetEffectiveValue();
}
public partial class GiftCouponPayment : PaymentComponent
{
private int couponValue;
private string myType;
public override int MyValue
{
get
{
return this.couponValue;
}
set
{
this.couponValue = value;
}
}
public override string MyType
{
get
{
return this.myType;
}
set
{
this.myType = value;
}
}
public override int GetEffectiveValue()
{
if (this.PaymentComponentID < 2000)
{
return 0;
}
return this.couponValue;
}
}
public partial class ClubCardPayment : PaymentComponent
{
private int cardValue;
private string myType;
public override int MyValue
{
get
{
return this.cardValue;
}
set
{
this.cardValue = value;
}
}
public override string MyType
{
get
{
return this.myType;
}
set
{
this.myType = value;
}
}
public override int GetEffectiveValue()
{
return this.cardValue;
}
}
public partial class Payment
{
public int PaymentID { get; set; }
public List<PaymentComponent> PaymentComponents { get; set; }
public DateTime PayedTime { get; set; }
}
//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
public NerdDinners(string connString): base(connString)
{
}
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<GiftCouponPayment> GiftCouponPayments { get; set; }
public DbSet<Payment> Payments { get; set; }
}
REFERENCE:
When using entity framework code-first mapping property to separate table, moves foreign key field
Override Entity Framework Entity Property
EntityFramework how to Override properties
http://weblogs.asp.net/manavi/archive/2011/04/24/associations-in-ef-4-1-code-first-part-4-table-splitting.aspx
http://www.robbagby.com/entity-framework/entity-framework-modeling-entity-splitting/
Entity Framework Mapping Scenarios - http://msdn.microsoft.com/en-us/library/cc716779.aspx
http://blogs.microsoft.co.il/blogs/gilf/archive/2009/03/06/entity-splitting-in-entity-framework.aspx
Implement MyType and MyValue directly in the base class. EF allows shared members to be implemented only in the base class. Members implemented in derived class use their own columns in the resulting table.
you haven't defined the ClubCardPayment dbset in the datacontext.
insert this and it should work
public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
You need to define the 2 classes that are actually implements of the abstract class, that's the only way EF will know the different classes and how to read/update/write instances of them.
(No need to map the abstract class in EF!).
This doesn't contribute to your question, but just a hint from my side:
Why do you implement MyValue and MyType explcitly in your derived classes? You can just put it into the abstract class, if the implementation is always the same...

Categories