Set property value from database - c#

I'm trying to set property values straight from database so i could initiate new object
Object AllValues = new Object(); and access it in code like this AllValues.MediumRunTime;
using Entity Framework and it's returning 0's which it should't.
Also ReSharper is throwing Field value is never assigned warning
My table is Values{name, value, units} and contains around ~100 records.
This is my AllValues class
using System.Linq;
namespace RunTimes
{
public class AllValues
{
private long value;
public long ShortRunTime
{
get => value;
set
{
using (var data = new ModelData())
{
var prop = data.Values.FirstOrDefault(c => c.name == "ShortRunTime");
value = prop.value;
}
}
}
public long MediumRunTime
{
get => value;
set
{
using (var data = new ModelData())
{
var prop = data.Values.FirstOrDefault(c => c.name == "MediumRunTime");
value = prop.value;
}
}
}
...
And im trying to access it on Program.cs like this
AllValues av = new AllValues();
Console.WriteLine($"Medium run time: {av.MediumRunTime}");

A property setter is not the place to 'set a field with the value from the database', you use it to set the value of the property after you first loaded it from the database.
So instead of your AllValues class as it currently stands, you would have a class with some simple properties, such as:
class AllValues
{
public string ShortRunTime { get; set; }
public string MediumRunTime { get; set; }
// ...
}
and then instantiate it like this:
var av = new AllValues();
using (var data = new ModelData())
{
av.ShortRunTime = data.Values.FirstOrDefault(c => c.name == "ShortRunTime")?.Value;
av.MediumRunTime = data.Values.FirstOrDefault(c => c.name == "MediumRunTime")?.Value;
// ...
}
After this you can work with your class in memory. The 'trick' is to separate concerns: your 'transformed class' (AllValues) and loading data from the database are 2 different concerns that should be treated separately.

Related

Trying to deep clone the row values with in the entity except Id

I have project entity structure like this as below with project name and project number and along with some list objects as well as properties.
public class DesignProject
{
[Key, GraphQLNonNullType]
public string ProjectNumber { get; set; }
public string Name { get; set; }
[Column(TypeName = "jsonb")]
public ProjectSectionStatus SectionStatuses { get; set; } = new ProjectSectionStatus();
[ForeignKey("AshraeClimateZone"), GraphQLIgnore]
public Guid? AshraeClimateZoneId { get; set; }
public virtual AshraeClimateZone AshraeClimateZone { get; set; }
[Column(TypeName = "jsonb")]
public List<ProjectObject<classA>> classAList{ get; set; } = new List<ProjectObject<classA>>();
[Column(TypeName = "jsonb")]
public List<ProjectObject<classB>> classBList{ get; set; } = new List<ProjectObject<classB>>();
......
......
......
Some more json columns
}
and the project object class like this
public class ProjectObject<T>
{
public Guid? Id { get; set; }
public T OriginalObject { get; set; }
public T ModifiedObject { get; set; }
[GraphQLIgnore, JsonIgnore]
public T TargetObject
{
get
{
return ModifiedObject ?? OriginalObject;
}
}
}
and ClassA entity structure like as below
public class ClassA
{
public string Edition { get; set; }
public string City { get; set; }
}
and i have some similar children entities (ClassA) same like as above, I want to copy the contents and statuses from one project entity to other project entity.
I have project entity with ProjectNumber 1212 and have another project having ProjectNumber like 23323 so i would like copy entire project contents from 1212 to 23323. So is there any way we can achieve this with C# and i am using .Net Core with Entity framework core.
Here the source design project that i am going to copy have same structure with destination design project and i am fine with overriding the destination project values and i don't want to update the project number here.
Could any one please let me know the how can i achieve this copying? Thanks in advance!!
Please let me know if i need to add any details for this question
Update : Deep copy related code
public InsertResponse<string> CopyBookmarkproject(string SourceProjectNumber, string destinationProjectNumber)
{
var sourceDesignProject = this._dbContext.DesignProject.Where(a => a.ProjectNumber == SourceProjectNumber).SingleOrDefault();
var destinationProject = this._dbContext.DesignProject.Where(a => a.ProjectNumber == destinationProjectNumber).SingleOrDefault();
CopyProject(sourceDesignProject, destinationProject);
// Need to update the Db context at here after deep copy
}
private void CopyProject(DesignProject sourceDesignProject, DesignProject destinationProject)
{
destinationProject.classAList= sourceDesignProject.classAList; // Not sure whether this copy will works
destinationProject.AshraeClimateZone = sourceDesignProject.AshraeClimateZone; // not sure whether this copy will works also
}
Updated solution 2:
var sourceDesignProject = this._dbContext.DesignProjects.AsNoTracking()
.Where(a => a.ProjectNumber == sourceProjectNumber)
.Include(a => a.PrimaryBuildingType)
.Include(a => a.AshraeClimateZone).SingleOrDefault();
var targetDesignProject = this._dbContext.DesignProjects.Where(a => a.ProjectNumber == targetProjectNumber).SingleOrDefault();
sourceDesignProject.ProjectNumber = targetDesignProject.ProjectNumber;
sourceDesignProject.SectionStatuses.AirSystemsSectionStatus = Entities.Enums.ProjectSectionStage.INCOMPLETE;
sourceDesignProject.SectionStatuses.CodesAndGuidelinesSectionStatus = Entities.Enums.ProjectSectionStage.INCOMPLETE;
sourceDesignProject.SectionStatuses.ExecutiveSummarySectionStatus = Entities.Enums.ProjectSectionStage.INCOMPLETE;
sourceDesignProject.SectionStatuses.ExhaustEquipmentSectionStatus = Entities.Enums.ProjectSectionStage.INCOMPLETE;
sourceDesignProject.SectionStatuses.InfiltrationSectionStatus = Entities.Enums.ProjectSectionStage.INCOMPLETE;
this._dbContext.Entry(sourceDesignProject).State = EntityState.Modified; // getting the below error at this line
this._dbContext.SaveChanges();
ok = true;
getting an error like as below
The instance of entity type 'DesignProject' cannot be tracked because another instance with the same key value for {'ProjectNumber'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
There's a simple and short solution which uses .AsNoTracking() for source entry, assigning destination's ProjectNumber to the source and then changing the EntityState as Modified for it. It will copy all the properties to the destination entry :
var sourceDesignProject = this._dbContext.DesignProject.AsNoTracking().Where(a => a.ProjectNumber == SourceProjectNumber).SingleOrDefault();
var destinationProject = this._dbContext.DesignProject.Where(a => a.ProjectNumber == destinationProjectNumber).SingleOrDefault();
sourceDesignProject.ProjectNumber = destinationProject.ProjectNumber;
this._dbContext.Entry(sourceDesignProject).State = System.Data.Entity.EntityState.Modified;
this._dbContext.SaveChanges();
Make sure your relational properties are loaded before.
You can make use of Reflection. As you have both, source and destination objects and are of the same type. You can just traverse through source object and fetch the properties and replace the values.
Something like :
if (inputObjectA != null && inputObjectB != null)
{
//create variables to store object values
object value1, value2;
PropertyInfo[] properties = inputObjectA.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
//get all public properties of the object using reflection
foreach (PropertyInfo propertyInfo in properties)
{
//get the property values of both the objects
value1 = propertyInfo.GetValue(inputObjectA, null);
value2 = propertyInfo.GetValue(inputObjectB, null);
}
Ignore the properties that are not needed to be copied. The method will have a signature like below :
CopyObjects(object inputObjectA, object inputObjectB, string[] ignorePropertiesList)
Two choices bring to mind for deep copy of objects:
1- Using BinaryFormatter (Needs Serializable attribute on your class, See original answer):
public static T DeepClone<T>(this T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T) formatter.Deserialize(ms);
}
}
2- Using JsonSerializers like NewtoneSoft:
public static T CloneJson<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
Update
Here's the code you can use with NewtonSoft Json library:
private void CopyProject(DesignProject sourceDesignProject, ref DesignProject destinationProject)
{
if (Object.ReferenceEquals(sourceDesignProject, null))
{
destinationProject = null;
return;
}
var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};
var destProjNumber = destinationProject.ProjectNumber;
destinationProject = JsonConvert.DeserializeObject<DesignProject>
(JsonConvert.SerializeObject(sourceDesignProject), deserializeSettings);
destinationProject.ProjectNumber = destProjNumber;
}
I always prefer using AutoMapper for these type of work. It's optimized and lower risk of making mistakes. And better than serialize/deserialize method since that would cause performance issues.
In your constructor (or in startup.cs and passing via dependency injection)
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<DesignProject, DesignProject>().ForMember(x => x.Id, opt => opt.Ignore());
cfg.CreateMap<ProjectObject<ClassA>, ProjectObject<ClassA>>().ForMember(x => x.Id, opt => opt.Ignore()));
cfg.CreateMap<ProjectObject<ClassA>, ProjectObject<ClassB>>().ForMember(x => x.Id, opt => opt.Ignore()));
cfg.CreateMap<ProjectObject<ClassB>, ProjectObject<ClassB>>().ForMember(x => x.Id, opt => opt.Ignore()));
cfg.CreateMap<ProjectObject<ClassB>, ProjectObject<ClassA>>().ForMember(x => x.Id, opt => opt.Ignore()));
});
var mapper = config.CreateMapper();
In your method :
var newProject = mapper.Map(sourceProject, new DesignProject());
OR
mapper.Map(sourceProject, targetProject);
This shouldn't work. If I understand you want to copy all elements from list into new list, but to make new object. Than maybe you can use extension method to clone complete list as described here link. For updating navigation properties check this post link.

How to return two specific columns from database using LINQ?

I have table called Asset. It has lot of columns. I only want to select two of them and use them separately.
Both of these columns are strings.
Linq query :
public static List<string> GetAssetIdsWithNames()
{
using (var db = DbManager.Get())
{
var result = db.Assets.SelectMany(i=> new[] { i.AssetName, i.AssetId }).Distinct().ToList();
return result;
}
}
Where I want to use them :
var assetList = AssetManager.GetAssetIdsWithNames();
//CURRENCYBOX IS A DROPDOWN
CurrencyBox.DataSource = assetList;
CurrencyBox.DataBind();
foreach (var item in assetList)
{
CurrencyBox.DataValueField = //asset id goes here
CurrencyBox.DataTextField =//asset name goes here
break;
}
You cannot access the anonymous type outside of the local scope.
Anonymous types can only be returned as Object outside their local scope and their properties inspected via reflection.
So in this scenario, you are likely better off to use a typed data contract and map from your Asset entity instead and then access it from your calling method.
Your use of SelectMany seems odd too, you probably are after Select instead.
public class AssetDto
{
public string Name { get;set; }
public string Id { get; set; }
}
public static List<AssetDto> GetAssetIdsWithNames()
{
using (var db = DbManager.Get())
{
var result = db.Assets.Select(i=> new AssetDto { Name = i.AssetName, Id = i.AssetId }).ToList();
return result;
}
}
You could use named value tuples for that so you don't need to create an extra class
public static List<(string Name, int Id)> GetAssetWithIds()
{
using (var db = DbManager.Get())
{
var result = db.Assets
.Select(a => new { a.AssetName, a.AssetId })
.Distinct().AsEnumerable()
.Select(a => (a.AssetName, a.AssetId))
.ToList();
return result;
}
}
You will need to add System.ValueTuple

LINQ select new list, pass parent class to property class

I'm not sure if I'm just searching the wrong thing or not, I just can't find an example of passing a parent class into a property of that class.
I've attempted to create a simple example which you can see below or view it at https://dotnetfiddle.net/uBGWgp.
I simply would like the Column class to know of its own parent Table, passing through the instance of it via the constructor (new Table()). See comment in code example where I wish to do this: /* Pass parent here? */
using System;
using System.Collections.Generic;
using System.Linq;
public class FakeDb{
public class FakeColumn{
public string ColumnName;
}
public class FakeTable{
public string TableName {get;set;}
public IEnumerable<FakeColumn> Columns {get;set;}
}
public IEnumerable<FakeTable> Tables {get;set;}
public FakeDb(){
Tables = new List<FakeTable>(){
new FakeTable(){
TableName = "People",
Columns = new List<FakeColumn>(){
new FakeColumn(){
ColumnName = "FirstName"
},
new FakeColumn(){
ColumnName = "LastName"
}
}
}
};
}
}
public class Table{
public Guid TableGuid = Guid.NewGuid();
public IEnumerable<Column> Columns;
}
public class Column{
private Table _myTable;
public Guid ColumneGuid = Guid.NewGuid();
public Column(Table hostTable){
_myTable = hostTable;
}
}
public class Program
{
public static void Main()
{
var fakeDb = new FakeDb();
var tableList = fakeDb.Tables.GroupBy(p => p.TableName, p => p, (tableName, values) => new {
TableName = tableName,
OtherValues = values
}).Select(p => new Table(){
Columns = p.OtherValues.Select(i => new Column(/* Pass parent here? */)).ToList()
}).ToList();
}
}
Because you are not holding a reference to the new Table object you are creating, there is no way to do it in a single-lined query. However changing it to a multi-lined lambda expression will make this possible. Save your newly created table in a variable and then pass that variable into your Column's constructor.
You will have to do something like this
.Select(p =>
{
var t = new Table();
t.Columns = p.OtherValues.Select(i => new Column(t)).ToList();
return t;
});
Demo: https://dotnetfiddle.net/1kKxMR

Reflection on IQueryable<T> can't find property

I created a custom gridview control and exported it into a dll so I can reuse it. Inside the dll I created a function to get the DataSource, I'm trying to fill a dropdown from there but is failing.
So on my website I have this
public partial class _Management : System.Web.UI.Page
{
public class _ManagementHelper
{
public int ID;
public string CompanyName;
public string ResourceName;
}
protected void Page_Load(object sender, EventArgs e)
{
ucGridViewEx.DataSource = ucGridViewEx_Source();
ucGridViewEx.DataBind();
}
private List<dynamic> ucGridViewEx_Source()
{
var source = dl.ComapniesResources.Select(x => new _ManagementHelper
{
ID = x.ResourceID,
CompanyName = x.Supplier1.SupplierName,
ResourceName = x.Name
});
return ucGridViewEx.GridViewExDataSource(source);
}
Then the custom control inside the dll have this relevant code
public List<dynamic> GridViewExDataSource<T>(IQueryable<T> query)
{
foreach (var column in this.Columns)
{
var gridViewExColumn = column as ColumnEx;
if (gridViewExColumn != null
&& gridViewExColumn.SearchType == SearchTypeEnum.DropDownList)
{
gridViewExColumn.DropDownDataSource = query.GetDropDownDataSource(gridViewExColumn.DataField);
}
}
return ((IQueryable<dynamic>)query).ToList<dynamic>();
}
Function GetDropDownDataSource() is inside another extension class inside the same dll as the gridview
internal static List<ListItem> GetDropDownDataSource<T>(this IQueryable<T> query,
string dataField)
{
var ddlSource = new List<ListItem>();
// x =>
var xParameter = Expression.Parameter(typeof(T), "x");
// x.Property
var propery = typeof(T).GetProperty(dataField);
// x => x.Property
var columnLambda = Expression.Lambda(Expression.Property(xParameter, propery), xParameter);
return ddlSource;
}
Code fails in this where I'm assingning the value to columnLambda because property is null, not because it does not exist (it does) because is not getting any property. I tried with GetProperties() and is not returning anything.
Is curious than this is happening since I moved to the DataSource to select into _ManagementHelper. I was using a dynamic ( Select(x => new {}) ) on ucGridViewEx_Source() before and it worked perfectly. Please don't provide the solution to keep using the dynamic because I need to allow both types, with dynamic and using custom objects.
_ManagementHelper has no property. It just contains three fields (as far as you told us). So GetPrperty returns nothing. Change the members of _ManagementHelper to properties:
public class _ManagementHelper
{
public int ID { get; set; }
public string CompanyName { get; set; }
public string ResourceName { get; set; }
}
I see one bug --
var source = dl.ComapniesResources.Select(x => new _ManagementHelper
{
// ResourceID = x.ResourceID, this was the old code
ID = x.ResourceID, // fixed code
CompanyName = x.Supplier1.SupplierName,
ResourceName = x.Name
});
also, where is ListItem defined?

Returning a collection of objects where an objects property matches any property from another collection of objects using LINQ-to-Entities

I've been searching all day and can't find a solution to this...
I have an EntityCollection of Communication objects which each have an instance of an Intention object(one-to-one).
I also have a User object which has many instances of UserLocation EntityObjects(one-to-many)
Intention objects have a property UID.
UserLocation objects have a property LID.
I want to write a LINQ expression which returns all Communication objects where the UID property of the Intention instance associated to a Communication object equals ANY LID property of ANY instance of a UserLocation instance for a User object.
I've tried this
return _context.Communications.Where
(u => u.Intention.UID.Equals
(user.UserLocations.Select
(p => p.LID)));
and this
return _context.Communications.Where
(u => user.UserLocations.Any
(x => x.LID.Equals
(u.Intention.UID)));
and this
var thislist = from Intentions in _context.Intentions
join UserLocations in user.UserLocations
on Intentions.UID equals UserLocations.LID
select Intentions.UID;
return _context.Communications.Where(u => u.Intention.Equals(thislist.Any()));
and this
var lidlist = user.UserLocations.Select(x => x.LID);
return _context.Communications.Where(x=> lidlist.Contains(x.Intention.UID)).ToList();
(this gives me an error on the Contains statement saying "Delegate System.Func<Communication,int,bool> does not take 1 argument", don't know how to fix)
Along with all these variations I have also:
modified my method to return IQueryable<Communication> and have also tried List<Communication> while appending ToList() to my queries.
Nothing works. Regardless of what I try I always end up with this exception
NotSupportedException was unhandled by user code
Unable to create a constant value of type 'PreparisCore.BusinessEntities.UserLocation'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
What am i doing wrong??
Given this code:
namespace CollectionsWithIntentions
{
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
internal class Program
{
#region Methods
private static void Main(string[] args)
{
var communications = new[]
{
new Communication { Intention = new Intention { UID = 1 } },
new Communication { Intention = new Intention { UID = 2 } },
new Communication { Intention = new Intention { UID = 3 } },
new Communication { Intention = new Intention { UID = 4 } },
};
var users = new[]
{
new User { UserLocations = new List<UserLocation>(new[] { new UserLocation { LID = 2 },new UserLocation{LID=5} }) },
new User { UserLocations = new List<UserLocation>(new[] { new UserLocation { LID = 3 } }) }
};
IEnumerable<Communication> res =
communications.Where(w => users.Any(a => a.UserLocations.Any(b=>b.LID == w.Intention.UID)));
foreach (Communication communication in res)
{
Trace.WriteLine(communication);
}
}
#endregion
}
internal class Communication
{
#region Public Properties
public Intention Intention { get; set; }
#endregion
#region Public Methods and Operators
public override string ToString()
{
return string.Concat("Communication-> Intention:", this.Intention.UID);
}
#endregion
}
internal class Intention
{
#region Public Properties
public int UID { get; set; }
#endregion
}
internal class User
{
#region Public Properties
public List<UserLocation> UserLocations { get; set; }
#endregion
}
internal class UserLocation
{
#region Public Properties
public int LID { get; set; }
#endregion
}
}
I get this result:
Communication-> Intention:2
Communication-> Intention:3
Am I missing anything?
From the last two compiler errors you have linked in one of your comments...
...I would conclude that Intention.UID is a nullable type int? and not a not-nullable int as you said in the comments. This indeed doesn't compile. Try to change your last query to:
var lidlist = user.UserLocations.Select(x => x.LID);
return _context.Communications
.Where(x => x.Intention.UID.HasValue
&& lidlist.Contains(x.Intention.UID.Value))
.ToList();
The other three queries do not work because user.UserLocations is a collection of a non-primitive custom type in memory (for the SQL query to be generated it is a "constant" value) and EF doesn't support to build a SQL query with such a constant custom type.

Categories

Resources