Automapper 8 Ignore Attribute doesn't work on child descendant - c#

I use AutoMapper 8.1.0 in a asp.net core project. I have an Automapper mapping that doesn't work as I expected. I reproduced the configuration so you can test it by yourself. So I have an ExpenseReport with a collection of ExpenseReportItem and this one with another collection. I have to keep the data of eTaxCollection after the mapping, but they are lost in the process.
So the question is why values of eTaxCollections are lost after calling _mapper.Map(vmodel, model) and how can I keep them?
The ignore attribute don't work. I also tried UseDestinationValue(). I lost 2 days trying to figure it out and I'm exhausted.
public void WeatherForecasts()
{
int[] excludeTaxes = new int[] { 2 };
var vmodel = new ExpenseReportCreateEditModel();
vmodel.Expenses.Add(new ExpenseReportItemModel()
{
ExcludeTaxIds = excludeTaxes,
Total = 12,
Id = 1
});
// fetch from bd
var model = new ExpenseReport();
// values will be lost after _mapper.Map...
var eTaxCollections = new HashSet<ExcludeExpenseReportItemTax>();
eTaxCollections.Add(new ExcludeExpenseReportItemTax()
{
TaxId = 1,
ExpenseReportItemId = 1
});
model.Items.Add(new ExpenseReportItem()
{
ExcludeTaxes = eTaxCollections,
ExpenseReportId = 1,
Id = 9
});
_mapper.Map(vmodel, model);
}
public class ExpenseReportCreateEditModelProfile : Profile
{
public ExpenseReportCreateEditModelProfile()
{
CreateMap<ExpenseReportCreateEditModel, ExpenseReport>()
.ForMember(d => d.Items, s => s.MapFrom(m => m.Expenses));
}
}
public class ExpenseReportItemModelProfile : Profile
{
public ExpenseReportItemModelProfile()
{
CreateMap<ExpenseReportItemModel, ExpenseReportItem>()
.ForMember(d => d.ExcludeTaxes, s => s.Ignore()); // <<<==== data are lost
}
}
public class ExpenseReportCreateEditModel
{
public int Id { get; set; }
public ICollection<ExpenseReportItemModel> Expenses { get; set; }
public ExpenseReportCreateEditModel()
{
Expenses = new HashSet<ExpenseReportItemModel>();
}
}
public class ExpenseReportItemModel
{
public int Id { get; set; }
public ICollection<int> ExcludeTaxIds { get; set; }
public decimal Total { get; set; }
public ExpenseReportItemModel()
{
ExcludeTaxIds = new HashSet<int>();
}
}
public class ExpenseReport
{
public int Id { get; set; }
public virtual ICollection<ExpenseReportItem> Items { get; set; }
public ExpenseReport()
{
Items = new HashSet<ExpenseReportItem>();
}
}
public class ExpenseReportItem
{
public int Id { get; set; }
public int ExpenseReportId { get; set; }
public virtual ICollection<ExcludeExpenseReportItemTax> ExcludeTaxes { get; set; }
public ExpenseReportItem()
{
ExcludeTaxes = new HashSet<ExcludeExpenseReportItemTax>();
}
}
public class ExcludeExpenseReportItemTax
{
public int ExpenseReportItemId { get; set; }
public virtual ExpenseReportItem ExpenseReportItem { get; set; }
public int TaxId { get; set; }
}
Thank you for any help
Edit
I execute the execution plan and perhaps this is the problem:
$typeMapDestination = ($dest ?? .New WebApplication1.Controllers.SampleDataController+ExpenseReportItem());
This is only way I can lost the values.
I have to find a solution now
Here the complete execution plan :
.If ($src == null) {
.Default(WebApplication1.Controllers.SampleDataController+ExpenseReportItem)
} .Else {
.Block() {
$typeMapDestination = ($dest ?? .New WebApplication1.Controllers.SampleDataController+ExpenseReportItem());
.Try {
.Block(System.Int32 $resolvedValue) {
.Block() {
$resolvedValue = .If (
$src == null || False
) {
.Default(System.Int32)
} .Else {
$src.Id
};
$typeMapDestination.Id = $resolvedValue
}
}
} .Catch (System.Exception $ex) {
.Block() {
.Throw .New AutoMapper.AutoMapperMappingException(
"Error mapping types.",
$ex,
.Constant<AutoMapper.TypePair>(AutoMapper.TypePair),
.Constant<AutoMapper.TypeMap>(AutoMapper.TypeMap),
.Constant<AutoMapper.PropertyMap>(AutoMapper.PropertyMap));
.Default(System.Int32)
}
};
$typeMapDestination
}
}

Related

Unable to fetch data from Azure Cosmos DB

I am having the below model class CallbackResponse.cs :
public class CallbackResponse
{
public Callback Data { get; set; }
}
public class Callback
{
public IEnumerable<ReviewInProgressActivityFeed> ActivitiesFeed { get; set; }
}
public class ReviewInProgressActivityFeed
{
public ReviewInProgressStatus ReviewerSession { get; set; }
}
public class ReviewInProgressStatus
{
public Guid ReviewActivityId { get; set; }
public string ReviewerName { get; set; }
public string ReviewComments { get; set; }
public DateTime ActivityDateTime { get; set; }
}
Sample Payload:
{
"data":
{
"activitiesFeed": [
{
"reviewerSession":
{
"reviewActivityId": "dd9937c3-7c01-4a4a-bc8d-05ef37b07ee5",
"ReviewerName": "Verification Team",
"reviewComments": "upload business verification document for further verification.",
"activityDateTime": "2021-03-31T18:34:26.5978962Z"
},
},
{
"reviewerSession":
{
"reviewActivityId": "dd9937c3-7c01-4a4a-bc8d-05ef37b07ee5",
"ReviewerName": "Other Team",
"reviewComments": "other documents required for verification.",
"activityDateTime": "2021-03-31T19:34:26.5978962Z"
},
}
]
}
}
I am trying to get the data from DB via the CallbackResponse model class. Please find the code for below.
public async Task<CallbackResponse> CallbackActivityFeedAsync(Guid Id)
{
CallbackResponse containerItems = new CallbackResponse();
IQueryable<HumanReviewRequest> query = cosmosReviewRequestContainer.GetItemLinqQueryable<HumanReviewRequest>(true);
query = query.Where(x => x.id == Id);
FeedIterator<HumanReviewRequest> feedIterator = query.ToFeedIterator();
while (feedIterator.HasMoreResults)
{
FeedResponse<HumanReviewRequest> r = await feedIterator.ReadNextAsync().ConfigureAwait(false);
foreach (HumanReviewRequest requestModel in r)
{
containerItems = new CallbackResponse
{
Data = new Callback
{
ActivitiesFeed = new List<ReviewInProgressActivityFeed>
{
new ReviewInProgressActivityFeed
{
ReviewerSession = new ReviewInProgressStatus
{
ReviewActivityId = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewActivityId).LastOrDefault(),
ActivityDateTime = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ActivityDateTime).LastOrDefault(),
ReviewComments = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerComments).LastOrDefault(),
ReviewerName = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerName).LastOrDefault()
}
}
}
}
};
}
}
return containerItems;
}
The Problem here is I could not able to fetch all the records present in activitiesFeed array in DB. Instead I could only able to fetch the last record in that array(I am using Azure Cosmos DB). Please help me in this.
HumanReviewRequest.cs (which is DB Class)for reference:
public class HumanReviewRequest
{
public Guid Id { get; set; }
public IEnumerable<ReviewActivity> ReviewActivities { get; set; }
public class ReviewActivity
{
public ReviewerSession ReviewerSession { get; set; }
public string UserComments { get; set; }
}
public class ReviewerSession
{
public Guid ReviewActivityId { get; set; }
public Guid ReviewerUserId { get; set; }
public DateTime ActivityDateTime { get; set; }
public string ReviewerComments { get; set; }
}
This is because you are creating a new List<ReviewInProgressActivityFeed>, a new ReviewInProgressActivityFeed and reading only the last element in activitiesFeed for each iteration:
ReviewerSession = new ReviewInProgressStatus
{
ReviewActivityId = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewActivityId).LastOrDefault(),
ActivityDateTime = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ActivityDateTime).LastOrDefault(),
ReviewComments = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerComments).LastOrDefault(),
ReviewerName = requestModel.ReviewActivities.Select(x => x.ReviewerSession.ReviewerName).LastOrDefault()
}
Try below code instead of the above code:
public async Task<CallbackResponse> CallbackActivityFeedAsync(Guid Id)
{
CallbackResponse containerItems = new CallbackResponse();
containerItems.Data = new Callback();
containerItems.Data.ActivitiesFeed = new List<ReviewInProgressActivityFeed>();
IQueryable<HumanReviewRequest> query = cosmosReviewRequestContainer.GetItemLinqQueryable<HumanReviewRequest>(true);
query = query.Where(x => x.id == Id);
FeedIterator<HumanReviewRequest> feedIterator = query.ToFeedIterator();
while (feedIterator.HasMoreResults)
{
FeedResponse<HumanReviewRequest> r = await feedIterator.ReadNextAsync().ConfigureAwait(false);
if (r != null || r.ReviewActivities != null)
{
foreach (HumanReviewRequest requestModel in r.ReviewActivities)
{
containerItems.Data.ActivitiesFeed.Add(
new ReviewInProgressActivityFeed
{
ReviewerSession = new ReviewInProgressStatus
{
ReviewActivityId = requestModel.ReviewerSession.ReviewActivityId,
ActivityDateTime = requestModel.ReviewerSession.ActivityDateTime,
ReviewComments = requestModel.ReviewerSession.ReviewerComments,
ReviewerName = requestModel.ReviewerSession.ReviewerName
}
});
}
}
}
};
}
}
return containerItems;
}

Auto calculate property within class

I have these two classes:
public class LeadPerformanceItem
{
public string name { get; set; }
public int visitors { get; set; }
public decimal visitorspercentoftotal
{
get
{
// ?
}
}
}
public class LeadPerformanceItemCollection
{
public List<LeadPerformanceItem> items {get;set;}
public int totalvisitors
{
get
{
return items.Sum(x => x.visitors);
}
}
}
Is there anyway my visitorspercentoftotal property could be automatically calculated as items are added and removed from the collection?
public class LeadPerformanceItem
{
public string name { get; set; }
public int Visitors { get; set; }
private int _totalVisitors = 0;
public void UpdateTotalVisitors(int total)
{
this._totalVisitors = total;
}
public decimal Visitorspercentoftotal => _totalVisitors != 0
? Convert.ToDecimal(Math.Round(((double) (Visitors * 100)) / _totalVisitors))
: 0;
}
public class LeadPerformanceItemCollection
{
public List<LeadPerformanceItem> Items { get; set; }
public void AddToItems(LeadPerformanceItem item)
{
Items.Add(item);
var total = Items.Sum(x => x.Visitors);
Items.AsParallel().ForAll(i => i.UpdateTotalVisitors(total));
}
public int totalvisitors
{
get { return Items.Sum(x => x.Visitors); }
}
}
[TestFixture]
public class Class1
{
[Test]
public void Test()
{
var leadPerformanceItemCollection = new LeadPerformanceItemCollection();
leadPerformanceItemCollection.Items=new List<LeadPerformanceItem>();
leadPerformanceItemCollection.AddToItems(new LeadPerformanceItem()
{
name = "test",
Visitors = 10
});
leadPerformanceItemCollection.AddToItems(new LeadPerformanceItem()
{
name = "test2",
Visitors = 25
});
Console.WriteLine(leadPerformanceItemCollection.Items[0].Visitorspercentoftotal);
Console.WriteLine(leadPerformanceItemCollection.Items[1].Visitorspercentoftotal);
}
}
result:
29%
71%
One way would be to inherit from List and hide the Add method and create your own and do the calculation there.
public class LeadPerformanceItemCollection : List<LeadPerformanceItem>
{
public new void Add(LeadPerformanceItem item)
{
//calculate percent of total here
base.Add(item);
}
}

FreshService API with Restsharp and Dynamic property names

I'm trying to use the FreshService API for assets and getting a list of assets returns JSON like the below. Notice that the properties inside "levelfield_values" have the id appended to the end of their property name.
I'm trying to pull the "license_expiry_date" for all assets but am having problems converting to objects to pull that field since the name changes with each item. Any suggestions?
{"config_item": {"agent_id": 215,
"asset_tag": batch_2017,
"assigned_on": "2014-07-18T03:54:18+05:30",
"ci_type_id": 3,
"created_at": "2014-07-25T14:25:04+05:30",
"deleted": false,
"department_id": 4,
"depreciation_id": null,
"description": null,
"disabled": false,
"display_id": 113,
"expiry_notified": false,
"id": 113,
"impact": 3,
"location_id": 21,
"name": "windows 7",
"salvage": null,
"trashed": false,
"updated_at": "2014-07-25T14:25:04+05:30",
"user_id": 214,
"department_name": "Finance",
"used_by": "Rachel",
"business_impact": "Medium",
"agent_name": "Andrea",
"levelfield_values": {
"product_3": 100,
"vendor_3": 43,
"cost_3": 4000,
"license_validity_3": 24,
"installation_date_3": "2014-07-25T14:25:04+05:30",
"license_expiry_date_3": "2016-07-25T00:00:00+05:30",
"license_key_3": "234_423_543_534",
"version_3": 2,
"license_type_3": "commercial",
"installed_machine_3": "Andrea’s computer",
"installation_path_3": null,
"last_audit_date_3": "2014-07-25T14:25:04+05:30"
},
"ci_type_name": "Software",
"product_name": "windows_os",
"vendor_name": "micosoft",
"state_name": null,
"location_name": "America" } }
You could do something like this
JsonConvert.DeserializeObject<JObject>(jsonString)["config_item"]["levelfield_values"]
.ToObject<JObject>()
.Properties()
.FirstOrDefault(x => x.Name.ToLower().Contains("license_validity"));
basically you;
navigate into config_item
navigate into levelfield_values
get all properties of navigated object (levelfield_values)
find a property that has a name like license_validity
That example Json is awful and incorrect, but.. here is how I'll make serialization seamless.
levelfield_values to definied as Expando object
defining a class for levelfield_values clean (no id in properties)
Add regenerative properties between Expando to defined class property and viceversa
I used visual studio paste special to write initially the class.
Example or fiddle:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var example = new Config
{
config_item = new Config_Item
{
LevelfieldValuesParshed = new CleanLevelfield_Values
{
Id = 2,
product = 1232
}
}
};
var serialized = JsonConvert.SerializeObject(example); // should have "product_2" : 2 inside
// VOILA deserialization with property parsing in a clean object
var deserialzed = JsonConvert.DeserializeObject<Config>(serialized);
if (example.config_item.LevelfieldValuesParshed.Id != deserialzed.config_item.LevelfieldValuesParshed.Id ||
example.config_item.LevelfieldValuesParshed.product != deserialzed.config_item.LevelfieldValuesParshed.product)
{
throw new Exception("Impossible to happen!!!");
}
}
}
public class Config
{
public Config_Item config_item { get; set; }
}
public class Config_Item
{
public int agent_id { get; set; }
public string asset_tag { get; set; }
public DateTime assigned_on { get; set; }
public int ci_type_id { get; set; }
public DateTime created_at { get; set; }
public bool deleted { get; set; }
public int department_id { get; set; }
public object depreciation_id { get; set; }
public object description { get; set; }
public bool disabled { get; set; }
public int display_id { get; set; }
public bool expiry_notified { get; set; }
public int id { get; set; }
public int impact { get; set; }
public int location_id { get; set; }
public string name { get; set; }
public object salvage { get; set; }
public bool trashed { get; set; }
public DateTime updated_at { get; set; }
public int user_id { get; set; }
public string department_name { get; set; }
public string used_by { get; set; }
public string business_impact { get; set; }
public string agent_name { get; set; }
// Regenerative property with backing filed => _levelfieldValuesParshed
[JsonIgnore] // Ignore property at serialization
public CleanLevelfield_Values LevelfieldValuesParshed
{
get
{
if (_levelfieldValuesParshed == null)
{
if (_levelfield_values != null) // if null everything is null
{
var propsByName = (IDictionary<string, object>)_levelfield_values; // Expando Object to dictionary
var product = propsByName.Keys.FirstOrDefault(x => x.StartsWith("product_", StringComparison.InvariantCultureIgnoreCase)); // user first to fail if not found, it can be smarter but it works
if (!string.IsNullOrEmpty(product))// hurray we know the id
{
if (int.TryParse(product.Replace("product_", ""), out int id)) // C# 7
{
// Cleaner code can be written (generic method to set get object props with reflection)
_levelfieldValuesParshed = new CleanLevelfield_Values
{
Id = id
};
_levelfieldValuesParshed.product = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"product_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"product_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.vendor = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"vendor_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"vendor_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.cost = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"cost_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"cost_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.license_validity = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_validity_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"license_validity_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.installation_date = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"installation_date_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToDateTime(propsByName.First(x => x.Key.Equals($"installation_date_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : DateTime.MinValue;
_levelfieldValuesParshed.license_expiry_date = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_expiry_date_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToDateTime(propsByName.First(x => x.Key.Equals($"license_expiry_date_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : DateTime.MinValue;
_levelfieldValuesParshed.license_key = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_key_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToString(propsByName.First(x => x.Key.Equals($"license_key_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : string.Empty;
_levelfieldValuesParshed.version = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"version_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToInt32(propsByName.First(x => x.Key.Equals($"version_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : 0;
_levelfieldValuesParshed.license_type = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"license_type_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToString(propsByName.First(x => x.Key.Equals($"license_type_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : string.Empty;
_levelfieldValuesParshed.installed_machine = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"installed_machine_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToString(propsByName.First(x => x.Key.Equals($"installed_machine_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : string.Empty;
_levelfieldValuesParshed.installation_path = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"installation_path_{id}", StringComparison.InvariantCultureIgnoreCase)))
? propsByName.First(x => x.Key.Equals($"installation_path_{id}", StringComparison.InvariantCultureIgnoreCase)).Value : new object();
_levelfieldValuesParshed.last_audit_date = !string.IsNullOrEmpty(propsByName.Keys.FirstOrDefault(x => x.Equals($"last_audit_date_{id}", StringComparison.InvariantCultureIgnoreCase)))
? Convert.ToDateTime(propsByName.First(x => x.Key.Equals($"last_audit_date_{id}", StringComparison.InvariantCultureIgnoreCase)).Value) : DateTime.MinValue;
}
}
}
}
return _levelfieldValuesParshed;
}
set
{
_levelfieldValuesParshed = value;
_levelfield_values = null;
}
}
private CleanLevelfield_Values _levelfieldValuesParshed;
// Regenerative Expando property with backing field => _levelfield_values
public System.Dynamic.ExpandoObject levelfield_values
{
get
{
if (_levelfieldValuesParshed != null)
{
_levelfield_values = new ExpandoObject();
// Cleaner code can be written with a foreach (generic method to set get object props with reflection)
var keValuesPairs = (IDictionary<string, object>)_levelfield_values;
keValuesPairs.Add($"product_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.product);
keValuesPairs.Add($"vendor_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.vendor);
keValuesPairs.Add($"cost_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.cost);
keValuesPairs.Add($"license_validity_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_validity);
keValuesPairs.Add($"installation_date_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.installation_date);
keValuesPairs.Add($"license_expiry_date_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_expiry_date);
keValuesPairs.Add($"license_key_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_key);
keValuesPairs.Add($"version_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.version);
keValuesPairs.Add($"license_type_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.license_type);
keValuesPairs.Add($"installed_machine_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.installed_machine);
keValuesPairs.Add($"installation_path_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.installation_path);
keValuesPairs.Add($"last_audit_date_{_levelfieldValuesParshed.Id}", _levelfieldValuesParshed.last_audit_date);
return _levelfield_values;
}
return null;
}
set
{
_levelfield_values = value;
_levelfieldValuesParshed = null; // remove cleaned object, it will regenerated itself when opened
}
}
private ExpandoObject _levelfield_values;
public string ci_type_name { get; set; }
public string product_name { get; set; }
public string vendor_name { get; set; }
public object state_name { get; set; }
public string location_name { get; set; }
}
public class CleanLevelfield_Values
{
public int Id { get; set; }
public int product { get; set; }
public int vendor { get; set; }
public int cost { get; set; }
public int license_validity { get; set; }
public DateTime installation_date { get; set; }
public DateTime license_expiry_date { get; set; }
public string license_key { get; set; }
public int version { get; set; }
public string license_type { get; set; }
public string installed_machine { get; set; }
public object installation_path { get; set; }
public DateTime last_audit_date { get; set; }
}
}

How to properly mapping object in fluent nhibernate

Its first time when I'm using ORM.
Here is my code:
public class EwidencjonowanaRzeczMap : ClassMap<EwidencjonowanaRzecz>
{
public EwidencjonowanaRzeczMap()
{
Id(c => c.Id);
References(m => m.WagaRef);
}
}
public class WagaMap : ClassMap<Waga>
{
public WagaMap()
{
Id(m => m.Id);
Map(m => m.WagaPrzedmiotu);
HasMany(m => m.ListaRzeczy).Inverse().Cascade.All();
}
}
public class EwidencjonowanaRzecz
{
public virtual int Id { get; set; }
public virtual Waga WagaRef { get; set; }
}
public class Waga
{
public virtual int Id { get; set; }
public virtual String WagaPrzedmiotu { get; set; }
public virtual IList<EwidencjonowanaRzecz> ListaRzeczy { get; set; }
public Waga()
{
ListaRzeczy = new List<EwidencjonowanaRzecz>();
}
}
Here is my function to select records:
public IList<T> SelectAllFromTable<T>()
{
IList<T> czyDanePoprawne = null;
try
{
_sesja = NHibernateHelper.CreateSessionFactory().OpenSession();
using (_sesja.BeginTransaction())
{
czyDanePoprawne = _sesja.CreateCriteria(typeof(T)).List<T>();
}
}
catch (Exception)
{
CzyNieWystapilyBledy = true;
}
return czyDanePoprawne;
}
Tables:
ewidencjonowanarzecz [ Id - PK , IdWagi - FK (Waga.Id)]
waga [ Id - PK , WagaPrzedmiotu ]
Every type Id's - INT(3)
WagaPrzedmiotu is VARCHAR(10)
How to read to list these 2 fields?:
Id (ewidencjonowanarzecz) and WagaPrzedmiotu (waga)
In my mapping:
EwidencjaList = ModelBazy.SelectAllFromTable<EwidencjonowanaRzecz>();
is empty.
What is wrong with my mapping?
If you want to select the table record you can try this :
_sesja = NHibernateHelper.CreateSessionFactory().OpenSession();
var tabledata = _sesja.CreateQuery("from EwidencjonowanaRzecz").List<EwidencjonowanaRzecz>();
If you want to check whether if it fetches the values, try this
string value = tabledata.WagaPrzedmiotu;

Records are doubled when adding referenced entities using Entity Framework

I have a problem, when i add a new entity of SessionImages, and then add another one (By uploading it), it gets doubled.
For example :
Adding an image :
Adding another one afterwards :
Here's my controller code :
[HttpPost]
public ActionResult SessionImages(FormCollection collection)
{
int SessionID = Convert.ToInt32(collection[0]);
Sessions SessionModel = sessionsRepo.GetSessionById(SessionID);
bool uploadSucceded = Utility.Utility.UploadImages(this, Request.Files, Server.MapPath(Path.Combine("~/Photos/Sessions", SessionModel.Name)));
for (int i = 0; i < Request.Files.Count; i++)
{
if (Request.Files[i].ContentLength == 0)
{
continue;
}
SessionImages ImageModel = new SessionImages
{
Name = Request.Files[i].FileName,
Path = Path.Combine("~/Photos/Sessions/", SessionModel.Name, "actual", Request.Files[i].FileName),
Session = SessionModel,
SessionID = SessionID
};
sessionImagesRepo.Add(SessionModel, ImageModel);
}
return RedirectToAction("SessionImages",SessionModel.ID);
}
Here's the sessionImagesRepo.Add method :
public SessionImages Add(Sessions SessionModel, SessionImages ImageModel)
{
try
{
context.Entry(ImageModel).State = System.Data.EntityState.Added;
SessionModel.PhotosCount = SessionModel.Images.Count;
context.Entry(SessionModel).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
catch
{
return null;
}
return ImageModel;
}
Here's my entities :
namespace FP.Domain.Entities
{
public class Sessions
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
public int PhotosCount { get; set; }
public string CoverPhoto { get; set; }
public List<SessionImages> Images { get; set; }
}
}
namespace FP.Domain.Entities
{
public class SessionImages
{
public int ImageID { get; set; }
public string Name { get; set; }
public string Path { get; set; }
public int SessionID { get; set; }
public Sessions Session { get; set; }
}
}
Here's the configuration :
namespace FP.Domain.Configurations
{
public class SessionsConfig : EntityTypeConfiguration<Sessions>
{
public SessionsConfig()
{
ToTable("Sessions");
Property(p => p.Name).IsRequired();
Property(p => p.PhotosCount).IsRequired();
Property(p => p.CoverPhoto).IsRequired();
Property(p => p.Date).HasColumnType("date");
}
}
}
namespace FP.Domain.Configurations
{
public class SessionImagesConfig : EntityTypeConfiguration<SessionImages>
{
public SessionImagesConfig()
{
ToTable("SessionImages");
HasKey(e => e.ImageID);
Property(p => p.Path).IsRequired();
HasRequired(i => i.Session)
.WithMany(s => s.Images)
.HasForeignKey(i => i.SessionID);
}
}
}
Try changing the repository method like this:
public SessionImages Add(Sessions SessionModel, SessionImages ImageModel)
{
try
{
SessionModel.Images.Add(ImageModel);
SessionModel.PhotosCount = SessionModel.Images.Count;
context.Entry(SessionModel).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
catch
{
return null;
}
return ImageModel;
}
And in the action remove the line that I've commented:
SessionImages ImageModel = new SessionImages
{
Name = Request.Files[i].FileName,
Path = Path.Combine("~/Photos/Sessions/", SessionModel.Name, "actual", Request.Files[i].FileName),
//Session = SessionModel,
SessionID = SessionID
};
I'd suggest you drop these lines, if you can workaround it (I can see the point though, in the count)...
context.Entry(ImageModel).State = System.Data.EntityState.Added;
SessionModel.PhotosCount = SessionModel.Images.Count;
context.Entry(SessionModel).State = System.Data.EntityState.Modified;
That's probably what's messing with it. I've seen similar problems more than once - and then I usually end up doing quite the opposite:
context.Entry(Parent).State = System.Data.EntityState.Unchanged;
Everything you're doing there is to get the Count working.
I can understand the optimization reasons, but you do have that count in the collection inherently. I know this is faster if you don't want to load etc.
I'm not sure what to suggest - but I'm suspecting that to be the 'culprit'.
Try w/o it, turn off and disregard count while testing. See what
happens. If that's it, then think about how to reorganize that a bit.

Categories

Resources