Why are all my foreign keys null? - c#

I've been playing around with Entity Framework for a while now. For some reason however the foreign keys all seem to be null untill I explicitely use the Include method to include them.
According to what I've read here:
Entity Framework 4.1 Virtual Properties
by making them virtual they should be lazy loaded (and not null).
This is my model:
class AltiGoalInfo
{
public int AltiGoalInfoId { get; set; }
public int ShotTime { get; set; } //Time it took from shooting till scoring
public virtual AltiPlayerInfo AltiPlayerInfo { get; set; }
public virtual AltiTeamEnum Team { get; set; }
public override string ToString()
{
double shotTime = ShotTime;
shotTime /= 1000;
shotTime = Math.Round(shotTime, 2);
return shotTime + " by " + AltiPlayerInfo;
}
}
class AltiPlayerInfo
{
public int AltiPlayerInfoId { get; set; }
public String VaporId { get; set; }
public String LastKnownNickname { get; set; }
public int LastKnownLevel { get; set; }
public int LastKnownAceRank { get; set; }
public Boolean LastKnownDemo { get; set; }
private IList<String> _knownIps = new List<String>();
public IList<String> KnownIps
{
get
{
return _knownIps;
}
set
{
_knownIps = value;
}
}
public string KnownIpsString
{
get
{
return String.Join(";", _knownIps);
}
set
{
_knownIps = value.Split(';').ToList();
}
}
public int Goals { get; set; }
public int MatchesPlayed { get; set; }
public int MatchesWon { get; set; }
public double CalcWinRate()
{
double mwon = MatchesWon;
double mplay = MatchesPlayed;
double result = mwon / mplay * 100.0;
return Math.Round(result, 0);
}
public DateTime FirstSeen { get; set; }
public DateTime LastSeen { get; set; }
public override string ToString()
{
return LastKnownNickname;
}
}
class AltiDbContext : DbContext
{
public DbSet<AltiPlayerInfo> AltiPlayerInfos { get; set; }
public DbSet<AltiGoalInfo> AltiGoalInfos { get; set; }
}
When I use the following code to acces the data the brrr variable is null:
var test = altiDbContext.AltiGoalInfos.First();
var brrr = test.AltiPlayerInfo;
When I however include in manually first, it does work:
var test = altiDbContext.AltiGoalInfos.Include(t => t.AltiPlayerInfo).First();
var brrr = test.AltiPlayerInfo;
Edit:
I've also verified the configuration of the DbContext, by default all settings in the configuration are set to true (which should enable lazy loading).

I found the solution, I had to make the model classes public

Related

Assign values from one model to another without checking null values each time

I need to assign values from one model to another only if values aren't null.
How can I simplify the code below? What pattern should I use?
DatabaseModel databaseModel = new DatabaseModel();
databaseModel.DatabaseModelProperty1 = 1;
databaseModel.DatabaseModelProperty1 = 2;
ContractModel contractModel = new ContractModel();
contractModel.ContractProperty1 = 5;
contractModel.ContractProperty2 = new ContractModel.InnerContractModel_1();
contractModel.ContractProperty2.InnerContract_1Property1 = 10;
// each time I need to write if and check null values:
if (contractModel.ContractProperty1.HasValue)
{
databaseModel.DatabaseModelProperty1 = contractModel.ContractProperty1.Value;
}
if (contractModel.ContractProperty2 != null)
{
if (contractModel.ContractProperty2.InnerContract_1Property1.HasValue)
{
databaseModel.DatabaseModelProperty2 = contractModel.ContractProperty2.InnerContract_1Property1.Value;
}
if (contractModel.ContractProperty2.InnerContract_1Property2.HasValue)
{
databaseModel.DatabaseModelProperty3 = contractModel.ContractProperty2.InnerContract_1Property2.Value;
}
}
// ..........
My first model:
public class DatabaseModel
{
public int DatabaseModelProperty1 { get; set; }
public int DatabaseModelProperty2 { get; set; }
public int DatabaseModelProperty3 { get; set; }
public int DatabaseModelProperty4 { get; set; }
public int DatabaseModelProperty5 { get; set; }
}
My second model:
public class ContractModel
{
public int? ContractProperty1 { get; set; }
public InnerContractModel_1 ContractProperty2 { get; set; }
public InnerContractModel_2 ContractProperty3 { get; set; }
public class InnerContractModel_1
{
public int? InnerContract_1Property1 { get; set; }
public int? InnerContract_1Property2 { get; set; }
}
public class InnerContractModel_2
{
public int? InnerContract_2Property1 { get; set; }
public int? InnerContract_2Property2 { get; set; }
}
}
Any ideas? I don't want to check each time null values but I have no idea what I should change.

Entity Framework: Can't save data because of tracking

I have got a complex class. Feedback and Steps. I am using SQL database and .NET Core 2. I can save main properties but can't save the sub class FeedbackSteps properties
public class FeedbackModel
{
[Key]
public int FeedBackID { get; set; }
public DateTime FBDate { get; set; }
public bool? VideoStatus { get; set; }
public string VideoDetail { get; set; }
public string PITFeedBack { get; set; }
public int ActivityID { get; set; }
public virtual ActivityModel Activity { get; set; }
public int ClientID { get; set; }
public virtual ClientModel Client { get; set; }
public int? SupportPlanID { get; set; }
public virtual SupportPlanModel SupportPlan { get; set; }
public int EmployeeID { get; set; }
public virtual Employee Employee { get; set; }
public bool FeedbackStatus { get; set; } = true;
virtual public List<FeedbackStepModel> FeedbackSteps { get; set; }
}
public class FeedbackStepModel
{
[Key]
public int FeedbackStepID { get; set; }
public int FeedbackID { get; set; } = 0;
public int SupportPlanID { get; set; }
public int StepNumber { get; set; }
public string StepDetail { get; set; }
public string AchievementStatus { get; set; }
public string AchievementComment { get; set; }
}
This is the post method. View returns Edited or Updated feedback and i just want to update the database with new data
[HttpPost]
public IActionResult Edit(FeedbackModel feedback)
{
if (ModelState.IsValid)
{
feedbackRepository.Save(feedback);
TempData["message"] = $"Feedback has been saved";
return RedirectToAction("Index");
}
}
After EDIT, I would like to save it...
public void Save(FeedbackModel feedback)
{
if (feedback.FeedBackID == 0)
{
context.FeedbackModels.Add(feedback);
}
else
{
FeedbackModel dbEntry = context.FeedbackModels.Include(s => s.FeedbackSteps).FirstOrDefault(a => a.FeedBackID == feedback.FeedBackID);
if (dbEntry != null)
{
dbEntry.FeedBackID = feedback.FeedBackID;
dbEntry.FBDate = feedback.FBDate;
dbEntry.VideoStatus = feedback.VideoStatus;
dbEntry.VideoDetail = feedback.VideoDetail;
dbEntry.SupportPlanID = feedback.SupportPlanID;
dbEntry.ActivityID = feedback.ActivityID;
dbEntry.PITFeedBack = feedback.PITFeedBack;
dbEntry.ClientID = feedback.ClientID;
dbEntry.EmployeeID = feedback.EmployeeID;
dbEntry.FeedbackStatus = feedback.FeedbackStatus;
dbEntry.FeedbackSteps = feedback.FeedbackSteps;
}
}
context.SaveChanges();
}
But I get this error all the time
The instance of entity type 'FeedbackStepModel' cannot be tracked because another instance with the key value '{FeedbackStepID: 1}' is already being tracked.
When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Your FeedbackModel update operation with children (FeedbackSteps) should be as follows:
FeedbackModel dbEntry = context.FeedbackModels.Include(s => s.FeedbackSteps).FirstOrDefault(a => a.FeedBackID == feedback.FeedBackID);
if (dbEntry != null)
{
dbEntry.FeedBackID = feedback.FeedBackID;
dbEntry.FBDate = feedback.FBDate;
dbEntry.VideoStatus = feedback.VideoStatus;
dbEntry.VideoDetail = feedback.VideoDetail;
dbEntry.SupportPlanID = feedback.SupportPlanID;
dbEntry.ActivityID = feedback.ActivityID;
dbEntry.PITFeedBack = feedback.PITFeedBack;
dbEntry.ClientID = feedback.ClientID;
dbEntry.EmployeeID = feedback.EmployeeID;
dbEntry.FeedbackStatus = feedback.FeedbackStatus;
dbEntry.FeedbackSteps.Clear(); // First you have to clear the existing feedBackSteps
foreach(FeedbackStep feedBackStep in feedback.FeedbackSteps)
{
dbEntry.FeedbackSteps.Add(feedBackStep); // You have to add new and updated feedBackStep here.
}
}
If dbEntry.FeedbackSteps.Clear(); does not work (may be in EF Core 2.0 or lower Clear() does not work) then replace dbEntry.FeedbackSteps.Clear(); with the following code:
foreach(FeedbackStep feedbackStepToBeRemoved in dbEntry.FeedbackSteps)
{
context.Remove(feedbackStepToBeRemoved);
}

IEnumerable of object returns JSON, but IEnumerable of similar object does not

I have two classes that are similar. I'm trying to see if I can simply use one. The first is in our DataModel assembly, and used in our DbContext.
public partial class type210_MotorCarrierFreightInvoice
{
public int MotorCarrierFreightInvoiceId { get; set; } // MotorCarrierFreightInvoiceId (Primary key)
public string InvoiceId { get; set; } // InvoiceId
public DateTime BillDate { get; set; } // BillDate
public string TrackingNumber { get; set; } // TrackingNumber
public decimal NetShipmentCharge { get; set; } // NetShipmentCharge
public long InterchangeTransactionDetailId { get; set; } // InterchangeTransactionDetailId
public long? BillToAddressId { get; set; } // BillToAddressId
public long? ConsigneeAddressId { get; set; } // ConsigneeAddressId
public long? ShipperAddressId { get; set; } // ShipperAddressId
// Foreign keys
public virtual interchange_TransactionDetail interchange_TransactionDetail { get; set; } // fk_Type210_MotorCarrierFreightInvoice_Interchange_TransactionDetail
public virtual typeAny_Address typeAny_Address_BillToAddressId { get; set; }
public virtual typeAny_Address typeAny_Address_ConsigneeAddressId { get; set; }
public virtual typeAny_Address typeAny_Address_ShipperAddressId { get; set; }
public type210_MotorCarrierFreightInvoice()
{
InitializePartial();
}
partial void InitializePartial();
}
The second class should represent the same object.
public class Type210MotorCarrierFreightInvoice
{
public int MotorCarrierFreightInvoiceId { get; set; }
public string InvoiceId { get; set; }
public DateTime BillDate { get; set; }
public string TrackingNumber { get; set; }
public decimal NetShipmentCharge { get; set; }
public long InterchangeTransactionDetailId { get; set; }
public Address BillToAddress { get; set; }
public Address ConsigneeAddress { get; set; }
public Address ShipperAddress { get; set; }
}
I'm working with a WebApi service that will expose the data via JSON. Basically what I'm trying is this:
[Route("api/Subscriber/Get210sNew/{transactionId}")]
[Route("api/Subscriber/Get210sNew/{transactionId}/{limit}")]
public IEnumerable<type210_MotorCarrierFreightInvoice> Get210sDOES_NOT_WORK(int transactionId, int limit = DefaultLimit)
{
var key = new QueryAfterId(transactionId, limit);
var dataService = new Type210DataService(_ediContext);
return dataService
.Get(key)
.Select(x => x);
}
[Route("api/Subscriber/Get210s/{transactionId}")]
[Route("api/Subscriber/Get210s/{transactionId}/{limit}")]
public IEnumerable<type210_MotorCarrierFreightInvoice> Get210sWorks(int transactionId, int limit = DefaultLimit)
{
var key = new QueryAfterId(transactionId, limit);
var dataService = new Type210DataService(_ediContext);
return dataService
.Get(key)
.Select(Map);
}
Here is the Map method:
private static Type210MotorCarrierFreightInvoice Map(
type210_MotorCarrierFreightInvoice dbNotice)
{
return new Type210MotorCarrierFreightInvoice
{
MotorCarrierFreightInvoiceId = dbNotice.MotorCarrierFreightInvoiceId,
InvoiceId = dbNotice.InvoiceId,
BillDate = dbNotice.BillDate,
ShipDate = dbNotice.ShipDate,
TrackingNumber = dbNotice.TrackingNumber,
PurchaseOrderNumber = dbNotice.PurchaseOrderNumber,
BillOfLading = dbNotice.BillOfLading,
NetShipmentCharge = dbNotice.NetShipmentCharge,
InterchangeTransactionDetailId = dbNotice.InterchangeTransactionDetailId,
BillToAddress = Mapper.MapToAddress(dbNotice.typeAny_Address_BillToAddressId),
ConsigneeAddress = Mapper.MapToAddress(dbNotice.typeAny_Address_ConsigneeAddressId),
ShipperAddress = Mapper.MapToAddress(dbNotice.typeAny_Address_ShipperAddressId)
};
}
Get210sWorks will return Json data to the browser. Get210sDOES_NOT_WORK does not. Both queries get the same number of records. But Get210sDOES_NOT_WORK shows no records in the browser window.
Why can't I return an IEnumerable of my DataModel class as Json?

using C# generic property type<T> in an EF code first class

For a variety of reasons I have to break my Symbol class into two distinct classes EquitySymbol and CommoditySymbol
In my DailyClose class I was originally using the SymbolId as my foreign key to navigate. I cant do that now that I have broken up Symbol into two different tables.
Here is my original DailyClose class:
public class DailyClose
{
public int DailyCloseId { get; set; }
[ForeignKey("Symbol")]
public int SymbolId { get; set; }
public Symbol Symbol {get; set;}
public DateTime Date { get; set; }
public decimal TodaysClose { get; set; }
}
What I want to do which obviously I can't is:
public class DailyClose
{
public int DailyCloseId { get; set; }
public int SymbolId { get; set; }
[DefaultValue(true)]
public bool IsEquity { get; set; }
public DateTime Date { get; set; }
public decimal TodaysClose { get; set; }
public CommoditySymbol CommodSymbol { get; set; }
public EquitySymbol EquitySymbol { get; set; }
//constructor
public DailyClose(int symbolId)
{
SymbolId = symbolId;
if (IsEquity)
{
EquitySymbol = new EquitySymbol(SymbolId);
CommodSymbol = null;
}
else
{
CommodSymbol = new CommoditySymbol(SymbolId);
EquitySymbol = null;
}
}
}
Well enable-migrations WAS telling me those types cant be null, but upon closing the solution and reopening it, it now allows my original methodology
BUT since those types (EquitySymbol & CommoditySymbol) cant be null this won't work. But If I could have a generic property, i.e. where T is either EquitySymbol or CommoditySymbol based on the IsEquity boolean that would be ideal.
Property<T>
and then have
public class DailyClose
{
public int DailyCloseId { get; set; }
public int SymbolId { get; set; }
[DefaultValue(true)]
public bool IsEquity { get; set; }
public DateTime Date { get; set; }
public decimal TodaysClose { get; set; }
public Property<T> WhichSymbolType { get; set; }
//constructor
public DailyClose(int symbolId)
{
SymbolId = symbolId;
if (IsEquity)
{
WhichSymbolType = new EquitySymbol(SymbolId);
}
else
{
WhichSymbolType = new CommoditySymbol(SymbolId);
}
}
}
Is this doable? If it is I'm not seeing how to accomplish this.

Using dynamic variables to pass in custom objects

I'm scraping data from a variety of pages on a site, and I want to assign points to a total score based on whether or not elements (H1s, alt tags, meta titles; that sort of thing) exist, are unique or are duplicates. I'd like to create a method that can do this for every element I scrape, which I am using custom classes to store.
public class PageData
{
[Key]
[Required]
public int Id { get; set; }
[Required]
public string PageUrl { get; set; }
public string Analytics { get; set; }
public bool Paginated { get; set; }
public bool Flash { get; set; }
public bool Iframe { get; set; }
public bool NoIndexFollow { get; set; }
public bool SchemaTag { get; set; }
public virtual ICollection<Platform> Platforms { get; set; }
public virtual ICollection<AltTag> AltTags { get; set; }
public virtual ICollection<Canonical> Canonicals { get; set; }
public virtual ICollection<MetaTitle> MetaTitles { get; set; }
public virtual ICollection<MetaDesc> MetaDescs { get; set; }
public virtual ICollection<BlogLocation> BlogLocations { get; set; }
public virtual ICollection<H1> H1s { get; set; }
public virtual ICollection<H2> H2s { get; set; }
public virtual ICollection<H3> H3s { get; set; }
public virtual ICollection<ViewState> ViewStates { get; set; }
}
public class H1
{
public H1() { }
public H1(int id, string h1)
{
this.Id = id;
this.H1String = h1;
}
public override string ToString()
{
return H1String;
}
[Key]
public int KeyId { get; set; }
public string H1String { get; set; }
[ForeignKey("PageData")]
public int Id { get; set; }
[ScriptIgnore]
public virtual PageData PageData { get; set; }
}
Method to try allocate scores
public void pageCheck(SiteData site, dynamic pageObj, int lowAssignedScore, int highAssignedScore, int totalScore)
{
List<string> uniqueCheckList = new List<string>();
bool uniqueCheck = true;
foreach (PageData page in site.PageDatas)
{
if (pageObj.Count != 0)
{
foreach (var modelObj in pageObj)
{
if (uniqueCheckList.Contains(modelObj.ToString()))
{
totalScore =+ lowAssignedScore;
uniqueCheck = false;
break;
}
uniqueCheckList.Add(modelObj.ToString());
}
if (uniqueCheck)
{
totalScore += highAssignedScore;
}
}
}
I'm instantiating a new page object to pass in which element of the page I want:
PageData page = new PageData();
pageCheck(site, page.H1s, 4, 6, totalScore);
When I pass in page.H1s it is coming through as:
{System.Collections.Generic.List < Bescoured.Models.PageModels.H1 > }
Is there a way to do what I am trying to do in c#? Or will it not let me due to the static nature of the language? I started off by creating a method that was specific to H1 then trying to make it generic but it looks like I need to make a method for each element.
EDIT:
An example of how I would do it if I had make a method for each element
foreach (PageData page in site.PageDatas)
{
if (page.H1s.Count != 0)
{
foreach (H1 h1 in page.H1s)
{
if (h1s.Contains(h1.H1String))
{
totalScore += 4;
uniqueCheck = false;
break;
}
h1s.Add(h1.H1String);
}
if (uniqueCheck)
{
totalScore += 6;
}
}
}
I see you're only using the ToString() method of a element. Why not pass a IEnumerable< string> into it?
public void pageCheck(SiteData site, IEnumerable<string> pageObj, int lowAssignedScore, int highAssignedScore, int totalScore)
Usage:
pageCheck(site, page.H1s.Select(item => item.ToString()), 4, 6, totalScore);
pageCheck(site, page.AltTags.Select(item => item.ToString()), 4, 6, totalScore);
I'll always try to avoid dynamics, unless there's no other option.

Categories