Let's say I have a an interface, which is basically a combination of two sub-interfaces. The idea behind this is, that I have two different API's. One which provides public information on a person. And once which provides the 'secret' information. It could look something like this:
public interface IPublicPersonData
{
// The ID is the key
int PersonId { get; set; }
// This property is specific to this part
string Name {get; set; }
}
public interface ISecretPersonData
{
// The ID is the key
int PersonId { get; set; }
// This property is specific to this part
decimal AnnualSalary{ get; set; }
}
public interface IPerson: IPublicPersonData, ISecretPersonData
{
// No new stuff, this is merely a combination of the two.
}
So basically I get two lists. One List<IPublicPersonData> and one List<ISecretPersonData>. I would like to join these into a single List<IPerson>, ideally using LINQ.
I cannot really find anything on how control the type of output from LINQ, based on the type of input, even if the logic is there (in the means of interfaces implementing interfaces).
public List<IPerson> JoinPersonData(
List<IPublicPersonData> publicData,
List<ISecretPersonData> secretData)
{
// What the heck goes here?
}
Say you wrote a method such as:
public ISomething CombinePersonWithSecret(
IPublicPersonData publicPerson,
ISecretPersonData secret)
{
if(publicPerson.PersonId != secret.PersonId)
{
throw ...;
}
//join 2 params into a single entity
return something;
}
Now you might...
IEnumerable<ISomething> secretivePeople = PublicPeople.Join(
SecretPersonData,
publicPerson => publicPerson.PersonId,
secret => secret.PersonId,
(publicPerson, secret) => CombinePersonWithSecret(publicPerson, secret))
The problem is not in the Join, it is in the IPerson you want to return. One of the parameters of the Join methods is used what to do with joined result.
You want to join them into a new object that implements IPerson. If you already have such an object: great, use that one, if you don't have it, here is an easy one:
public PersonData : IPerson // and thus also IPublicPersonData and ISecretPersonData
{
// this PersonData contains both public and secret data:
public IPublicPersonData PublicPersonData {get; set;}
public ISecretPersnData SecretPersonData {get; set;}
// implementation of IPerson / IPublicPersonData / ISecretPersonData
int PersonId
{
get {return this.PublicPersonData.Id; }
set
{ // update both Ids
this.PublicPersonData.Id = value;
this.SecreatPersonData.Id = value;
}
}
public string Name
{
get { return this.PublicPersonData.Name; },
set {this.PublicPersonData.Name = value;}
}
public decimal AnnualSalary
{
get {return this.SecretPersonData.AnnualSalary;},
set {this.SecretPersnData.AnnualSalary = value;
}
}
This object requires no copying of the values of the puclic and secret person data. Keep in mind however, if you change values, the original data is changed. If you don't want this, you'll need to copy the data when creating the object
IEnumerable<IPublicPersonData> publicData = ...
IEnumerable<ISecretPersonData> secretData = ...
// Join these two sequences on same Id. Return as an IPerson
IEnumerable<IPerson> joinedPerson = publicData // take the public data
.Join(secretData, // inner join with secret data
publicPerson => publicPerson.Id, // from every public data take the Id
secretPerson => secretPerson.Id, // from every secret data take the Id
(publicPerson, secretPerson) => new PersonData() // when they match make a new PersonData
{
PublicPersonData = publicPerson,
SecretPersnData = secretPerson,
});
LINQ's Join method does the job for you. Assuming there is a Person : IPerson class, here is two ways to implement your JoinPersonData method:
public static IEnumerable<IPerson> LiteralJoinPersonData(List<IPublicPersonData> publics, List<ISecretPersonData> secrets)
{
return from p in publics
join s in secrets on p.PersonId equals s.PersonId
select new Person(p.PersonId, p.Name, s.AnnualSalary);
}
public static IEnumerable<IPerson> FunctionalJoinPersonData(List<IPublicPersonData> publics, List<ISecretPersonData> secrets)
{
return publics
.Join<IPublicPersonData, ISecretPersonData, int, IPerson>(
secrets,
p => p.PersonId,
s => s.PersonId,
(p, s) => new Person(p.PersonId, p.Name, s.AnnualSalary));
}
Related
Let's say I have a class:
public class Customer
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
And now I want to create a generic Get() method that might query Customer or any one of several other classes that also have a [key] field defined.
public T Get<T>(int id)
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(i => i. ????? = id);
}
I'm not sure how to use Linq to generically specify the [key] field.
Thanks!
Hope this helps:
public interface IBase
{
int Id { get; }
}
public class Customer : IBase
{
public string Name { get; set; }
public int Id { get ; set ; }
}
public T Get<T>(int id) where T : IBase
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(i => i.Id == id);
}
Just implement the interface IBase in all other classes.
For what is worth I think using contracts is a better way to solve this. But in case you or someone else actually need to check for the attribute here's the answer:
public static T Get<T>(int id)
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(
item => (int)item.GetType()
.GetProperties()
.FirstOrDefault(
p => Attribute.IsDefined(p, typeof(KeyAttribute))
).GetValue(item) == id
);
}
As far a this part of your question:
I'm not sure how to use Linq to generically specify the [key] field.
The attribute is KeyAttribute you can know that by navigating to the definition (pressing F12 if you're using VS or checking the docs in case your editor doesn't support this feature.
Things to consider:
this is using Reflection reasonably heavily, so it will never have the best performance. That being said you can cache the result from GetProperties() somewhere for faster lookups.
It's hardcoding the cast to int but it appears that's what you're after.
If the collection is null it'll throw an exception.
I have an application where i have say 10 objects of different types. I wish to have them in same list and iterate through them on many occasions. I cant push them into one list because they are of different types. So i created an interface and created a property that all objects share. Now i have the list of objects and type of the list is the "interface". When i iterate through the object, i can't access the specific properties of the object because the compiler will only know at runtime what object it is. So if i try to code Object_A.Name, visual studio will show error because it doesn't know they type of object. I can obviously do an if else or something similar to find the type of object and cast it, but i want to know of there is a better way, or if this whole approach of having an interface is wrong and if i should have begun in a different direction.
In the code below, i want to get the Devname, which i can't because its not part of the interface, but belongs to every object. I could make it part of the interface, but every now and then i may need to get a specific property. hence wanting to know if there is a way to do it.
foreach (ICommonDeviceInterface device in Form1.deviceList)
{
if (device.DevName.Equals(partnername))
{
return device.Port[portNo].PortRef;
}
}
One way you could do this is by using reflection to try to get the property value of a named property from an object, using a helper method like:
public static object GetPropValue(object src, string propName)
{
return src?.GetType().GetProperty(propName)?.GetValue(src, null);
}
Credit for above code goes to: Get property value from string using reflection in C#
This requires no checking types or casting, it just returns the value of the property, or null if it doesn't contain the property.
In use it might look like:
private static void Main()
{
// Add three different types, which all implement the same interface, to our list
var devices = new List<ICommonDeviceInterface>
{
new DeviceA {DevName = "CompanyA", Id = 1},
new DeviceB {DevName = "CompanyB", Id = 2},
new DeviceC {Id = 3},
};
var partnerName = "CompanyB";
foreach (var device in devices)
{
// Try to get the "DevName" property for this object
var devName = GetPropValue(device, "DevName");
// See if the devName matches the partner name
if (partnerName.Equals(devName))
{
Console.WriteLine($"Found a match with Id: {device.Id}");
}
}
}
Classes used for the sample above:
interface ICommonDeviceInterface
{
int Id { get; set; }
}
class DeviceA : ICommonDeviceInterface
{
public int Id { get; set; }
public string DevName { get; set; }
}
class DeviceB : ICommonDeviceInterface
{
public int Id { get; set; }
public string DevName { get; set; }
}
class DeviceC : ICommonDeviceInterface
{
public int Id { get; set; }
}
Use "as" and "is" to know what type of interface
public class A : ICommonDeviceInterface
{
public int AMember;
}
public class B :ICommonDeviceInterface
{
public int BMember;
}
foreach (ICommonDeviceInterface device in Form1.deviceList)
{
if(device is A)
{
A a = device as A;
a.AMember = 100;
}
else if(device is B)
{
B b = device as B;
b.BMember = 123;
}
}
I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());
I need help as to how do I go about the structure of classes. How do I use Indexers? I want to have something like
Company.Employees[empId].Employee["Designation"].Salary
To be more specific something like
Grid.Rows[rowIndex].Columns["CurrentColumnName"].Width
Add a method like
public string this[string s]
{
get{
if(s == ...)
return this.property;
}
}
Yet, this seems to be more a Situation for Collections, but
see here for a complete example.
Actually indexers are used to get element by index, and your EmpId is not a good candidate for indexing as these may be compost or non sequential.
If you still want to use it here is the code. It will mimic as Indexer but its modified version.
class Employee
{
public int EmpId { get; set; }
public float Salary { get; set; }
public string Designation { get; set; }
}
class Employees
{
List<Employee> EmpList = new List<Employee>();
public Employee this[int empId]
{
get
{
return EmpList.Find(x => x.EmpId == empId);
}
}
}
I would rather have a method because I can make it generic.
public T GetPropertyValue<T>(string property)
{
var propertyInfo = GetType().GetProperty(property);
return (T)propertyInfo.GetValue(this, null);
}
var emp = employee.GetPropertyValue<Employee>("Designation");
var salary = emp.Salary;
That said... Be careful for having so many dot notations. When you get that NullReferenceException on your line in a log file, it is very difficult to find out what exactly was null. So rather break things up a bit and have more lines then you have less trouble of resolving bugs.
I have a class that needs a property set inside a LINQ-to-SQL query. My first attempt was to have a "setter" method that would return the object instance and could be used in my select, like this:
public partial class Foo
{
public DateTime RetrievalTime { get; set; }
public Foo SetRetrievalTimeAndReturnSelf ( DateTime value )
{
RetrievalTime = value;
return this;
}
}
....
from foo in DataContext.GetTable<Foo> select foo.SetRetrievalTimeAndReturnSelf();
Unfortunately, such a query throws an exception like this: "System.NotSupportedException: Method 'Foo.SetRetrievalTime(System.DateTime)' has no supported translation to SQL".
Is there any alternative to converting the result to a list and iterating over it? The query is used in a custom "Get" method that wraps the DataContext.GetTable method, so will be used as the base for many other queries. Immediately converting a potentially-large result set to a list would not be optimal.
UPDATE
Here's a better example of what I'm trying to do, updated with Jason's proposed solution:
protected IQueryable<T> Get<T>() where T : class, ISecurable
{
// retrieve all T records and associated security records
var query = from entity in DataContext.GetTable<T> ()
from userEntityAccess in DataContext.GetTable<UserEntityAccess> ()
where userEntityAccess.SysUserId == CurrentUser.Id
&& entity.Id == userEntityAccess.EntityId
&& userEntityAccess.EntityClassName == typeof ( T ).Name
select new { entity, userEntityAccess };
return query.AsEnumerable ()
.Select ( item =>
{
item.entity.CanRead = item.userEntityAccess.CanRead;
item.entity.CanWrite = item.userEntityAccess.CanWrite;
item.entity.CanDelete = item.userEntityAccess.CanDelete;
return item.entity;
} ).AsQueryable ();
}
public interface ISecurable
{
int Id { get; set; }
bool CanRead { get; set; }
bool CanWrite { get; set; }
bool CanDelete { get; set; }
}
UserEntityAccess is a cross-reference table between a user and a business object record (i.e. an entity). Each record contains fields like "CanRead", "CanWrite", and "CanDelete", and determines what a specific user can do with a specific record.
ISecurable is a marker interface that must be implemented by any LINQ-to-SQL domain class that needs to use this secured Get method.
var projection = DataContext.GetTable<Foo>
.AsEnumerable()
.Select(f => f.SetRetrievalTimeAndReturnSelf());
This will then perform the invocation of SetRetrievalTimeAndReturnSelf for each instance of Foo in DataContext.GetTable<Foo> when the IEnumerable<Foo> projection is iterated over.
What do you need to know the time that object was yanked of the database for? That's potentially smelly.