Skip to content

Commit 68b04e1

Browse files
authored
Merge pull request #151 from freakingawesome/gather-unmapped-cells
Add a way to gather unmapped cells
2 parents 614cdd2 + 666f911 commit 68b04e1

7 files changed

Lines changed: 111 additions & 7 deletions

File tree

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,40 @@ var excel = new ExcelQueryFactory("excelFileName");
216216
excel.StrictMapping = StrictMappingType.Both;
217217
```
218218

219+
### Retaining Values from Unmapped Columns
220+
221+
If you are using `None` or `ClassStrict` mapping, you can retain unmapped columns by implementing the `IContainsUnmappedCells` interface. This will put all values from the unmapped columns into a dictionary on your class named `UnmappedCells`.
222+
223+
Let's say the only field you're guaranteed to have is a `Name` column, and the rest of the columns can be different per spreadsheet. You could write your `Company` class like this, implementing `IContainsUnmappedCells`:
224+
225+
```c#
226+
public class Company : IContainsUnmappedCells
227+
{
228+
public string Name { get; set; }
229+
public IDictionary<string, Cell> UnmappedCells { get; } = new Dictionary<string, Cell>();
230+
}
231+
```
232+
233+
Given the following data set:
234+
235+
Name | CEO | EmployeeCount | StartDate
236+
-------------------------------------- | ------------- | ------------- | ----------
237+
ACME | Bugs Bunny | 25 | 1918-11-11
238+
Word Made Flesh | Chris Heuertz | | 1994-08-08
239+
Anderson University | James Edwards | | 1917-09-01
240+
241+
You can query normally and all other fields will be available in the `UnmappedCells` dictionary:
242+
243+
```c#
244+
var company = from c in excel.Worksheet<Company>()
245+
where c.Name == "ACME"
246+
select c;
247+
248+
// company.UnmappedCells["CEO"] == "Bugs Bunny"
249+
// company.UnmappedCells["EmployeeCount"].Cast<int>() == 25
250+
// company.UnmappedCells["StartDate"].Cast<DateTime>() == new DateTime(1918, 11, 11)
251+
```
252+
219253
## Manually setting the database engine
220254

221255
LinqToExcel can use the Jet or Ace database engine, and it automatically determines the database engine to use by the file extension. You can manually set the database engine with the `DatabaseEngine` property
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+

2+
using System.Collections.Generic;
3+
4+
namespace LinqToExcel.Tests
5+
{
6+
class CompanyNameWithUnmappedCells : IContainsUnmappedCells
7+
{
8+
public CompanyNameWithUnmappedCells()
9+
{
10+
UnmappedCells = new Dictionary<string, Cell>();
11+
}
12+
13+
public string Name { get; set; }
14+
public IDictionary<string, Cell> UnmappedCells { get; private set; }
15+
}
16+
}

src/LinqToExcel.Tests/ExcelQueryFactoryTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,24 @@ public void StrictMapping_Not_Explicitly_Set_with_additional_class_properties_do
285285
Assert.AreEqual(1, companies.Count);
286286
}
287287

288+
[Test]
289+
public void IContainsUnmappedCells_FillsInUnmappedCells()
290+
{
291+
var excel = new ExcelQueryFactory(_excelFileName, new LogManagerFactory());
292+
excel.AddMapping<Company>(x => x.IsActive, "Active");
293+
294+
var companies = (from c in excel.Worksheet<CompanyNameWithUnmappedCells>()
295+
where c.Name == "ACME"
296+
select c).ToList();
297+
298+
Assert.AreEqual(1, companies.Count);
299+
Assert.AreEqual("ACME", companies[0].Name);
300+
Assert.AreEqual(3, companies.First().UnmappedCells.Count);
301+
Assert.AreEqual("Bugs Bunny", companies.First().UnmappedCells["CEO"].Value);
302+
Assert.AreEqual(25, companies.First().UnmappedCells["EmployeeCount"].Cast<int>());
303+
Assert.AreEqual(new DateTime(1918, 11, 11), companies.First().UnmappedCells["StartDate"].Cast<DateTime>());
304+
}
305+
288306
[Test]
289307
public void TrimSpaces_Start_TrimsWhiteSpacesAtTheBeginning()
290308
{

src/LinqToExcel.Tests/LinqToExcel.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
<Compile Include="CompanyNullable.cs" />
9393
<Compile Include="CompanyWithColumnAnnotations.cs" />
9494
<Compile Include="CompanyWithCity.cs" />
95+
<Compile Include="CompanyNameWithUnmappedCells.cs" />
9596
<Compile Include="ConfiguredWorksheetName_SQLStatements_UnitTests.cs" />
9697
<Compile Include="ConfiguredWorksheetName_IntegrationTests.cs" />
9798
<Compile Include="Convention_SQLStatements_UnitTests.cs" />
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Collections.Generic;
2+
3+
namespace LinqToExcel
4+
{
5+
/// <summary>
6+
/// Implement this interface to receive values for cells that
7+
/// were not mapped.
8+
/// </summary>
9+
public interface IContainsUnmappedCells
10+
{
11+
IDictionary<string, Cell> UnmappedCells { get; }
12+
}
13+
}

src/LinqToExcel/LinqToExcel.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
<ItemGroup>
8585
<Compile Include="Attributes\ExcelColumnAttribute.cs" />
8686
<Compile Include="Domain\Cell.cs" />
87+
<Compile Include="Domain\IContainsUnmappedCells.cs" />
8788
<Compile Include="Domain\RowNoHeader.cs" />
8889
<Compile Include="Domain\StrictMappingException.cs" />
8990
<Compile Include="Exceptions\ExcelException.cs" />

src/LinqToExcel/Query/ExcelQueryExecutor.cs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -316,29 +316,50 @@ private IEnumerable<object> GetTypeResults(IDataReader data, IEnumerable<string>
316316
var props = fromType.GetProperties();
317317
if (_args.StrictMapping.Value != StrictMappingType.None)
318318
this.ConfirmStrictMapping(columns, props, _args.StrictMapping.Value);
319+
var gatherUnmappedCells = typeof(IContainsUnmappedCells).IsAssignableFrom(fromType);
319320

320321
var currentRowNumber = 0;
321322
while (data.Read())
322323
{
323324
currentRowNumber++;
324325
var result = Activator.CreateInstance(fromType);
325-
foreach (var prop in props)
326+
327+
var propMapping = props.Select(prop => new
326328
{
327-
var columnName = (_args.ColumnMappings.ContainsKey(prop.Name)) ?
329+
prop,
330+
columnName = (_args.ColumnMappings.ContainsKey(prop.Name)) ?
328331
_args.ColumnMappings[prop.Name] :
329-
prop.Name;
332+
prop.Name
333+
});
334+
335+
foreach (var mapping in propMapping)
336+
{
330337
try
331338
{
332-
if (columns.Contains(columnName))
339+
if (columns.Contains(mapping.columnName))
333340
{
334-
var value = GetColumnValue(data, columnName, prop.Name, fromType.Name).Cast(prop.PropertyType);
341+
var value = GetColumnValue(data, mapping.columnName, mapping.prop.Name, fromType.Name).Cast(mapping.prop.PropertyType);
335342
value = TrimStringValue(value);
336-
result.SetProperty(prop.Name, value);
343+
result.SetProperty(mapping.prop.Name, value);
337344
}
338345
}
339346
catch (Exception exception)
340347
{
341-
throw new Exceptions.ExcelException(currentRowNumber, columnName, exception);
348+
throw new Exceptions.ExcelException(currentRowNumber, mapping.columnName, exception);
349+
}
350+
}
351+
352+
if (gatherUnmappedCells)
353+
{
354+
var gatherer = (IContainsUnmappedCells)result;
355+
if (gatherer.UnmappedCells != null)
356+
{
357+
foreach (var col in columns.Except(propMapping.Select(x => x.columnName)))
358+
{
359+
var value = data[col];
360+
value = TrimStringValue(value);
361+
gatherer.UnmappedCells[col] = new Cell(value);
362+
}
342363
}
343364
}
344365
results.Add(result);

0 commit comments

Comments
 (0)